SQL MAX de várias colunas?

Como você retorna 1 valor por linha do máximo de várias colunas:

Nome da mesa

[Number, Date1, Date2, Date3, Cost] 

Eu preciso retornar algo assim:

 [Number, Most_Recent_Date, Cost] 

Inquerir?

Bem, você pode usar a instrução CASE:

 SELECT CASE WHEN Date1 >= Date2 AND Date1 >= Date3 THEN Date1 WHEN Date2 >= Date1 AND Date2 >= Date3 THEN Date2 WHEN Date3 >= Date1 AND Date3 >= Date2 THEN Date3 ELSE Date1 END AS MostRecentDate 

[Para o Microsoft SQL Server 2008 e superior, você pode considerar a resposta mais simples de Sven abaixo.]

Aqui está outra boa solução para a funcionalidade Max usando T-SQL e SQL Server

 SELECT [Other Fields], (SELECT Max(v) FROM (VALUES (date1), (date2), (date3),...) AS value(v)) as [MaxDate] FROM [YourTableName] 

Se você estiver usando o MySQL, você pode usar

 SELECT GREATEST(col1, col2 ...) FROM table 

Existem mais 3 methods onde o UNPIVOT (1) é o mais rápido de longe, seguido pelo Simulado Unpivot (3) que é muito mais lento que (1) mas ainda mais rápido que (2)

 CREATE TABLE dates ( number INT PRIMARY KEY , date1 DATETIME , date2 DATETIME , date3 DATETIME , cost INT ) INSERT INTO dates VALUES ( 1, '1/1/2008', '2/4/2008', '3/1/2008', 10 ) INSERT INTO dates VALUES ( 2, '1/2/2008', '2/3/2008', '3/3/2008', 20 ) INSERT INTO dates VALUES ( 3, '1/3/2008', '2/2/2008', '3/2/2008', 30 ) INSERT INTO dates VALUES ( 4, '1/4/2008', '2/1/2008', '3/4/2008', 40 ) GO 

Solução 1 ( UNPIVOT )

 SELECT number , MAX(dDate) maxDate , cost FROM dates UNPIVOT ( dDate FOR nDate IN ( Date1, Date2, Date3 ) ) as u GROUP BY number , cost GO 

Solução 2 (subconsulta por linha)

 SELECT number , ( SELECT MAX(dDate) maxDate FROM ( SELECT d.date1 AS dDate UNION SELECT d.date2 UNION SELECT d.date3 ) a ) MaxDate , Cost FROM dates d GO 

Solução 3 (Simulado UNPIVOT )

 ;WITH maxD AS ( SELECT number , MAX(CASE rn WHEN 1 THEN Date1 WHEN 2 THEN date2 ELSE date3 END) AS maxDate FROM dates a CROSS JOIN ( SELECT 1 AS rn UNION SELECT 2 UNION SELECT 3 ) b GROUP BY Number ) SELECT dates.number , maxD.maxDate , dates.cost FROM dates INNER JOIN MaxD ON dates.number = maxD.number GO DROP TABLE dates GO 

Qualquer uma das duas amostras abaixo funcionará:

 SELECT MAX(date_columns) AS max_date FROM ( (SELECT date1 AS date_columns FROM data_table ) UNION ( SELECT date2 AS date_columns FROM data_table ) UNION ( SELECT date3 AS date_columns FROM data_table ) ) AS date_query 

O segundo é um complemento da resposta de lassevk .

 SELECT MAX(MostRecentDate) FROM ( SELECT CASE WHEN date1 >= date2 AND date1 >= date3 THEN date1 WHEN date2 >= date1 AND date2 >= date3 THEN date2 WHEN date3 >= date1 AND date3 >= date2 THEN date3 ELSE date1 END AS MostRecentDate FROM data_table ) AS date_query 

A Função Escalar causa todos os tipos de problemas de desempenho, portanto, é melhor envolver a lógica em uma Função de Valor da Tabela Inline, se possível. Esta é a function que usei para replace algumas Funções Definidas pelo Usuário que selecionavam as datas Mín / Máx de uma lista de até dez datas. Quando testado em meu dataset de 1 Milhão de linhas, a Função Escalar levou mais de 15 minutos antes de eu matar a consulta. O Inline TVF levou 1 minuto, o mesmo tempo que a seleção do conjunto de resultados em uma tabela temporária. Para usar essa chamada, chame a function de uma subconsulta no SELECT ou em uma CROSS APPLY.

 CREATE FUNCTION dbo.Get_Min_Max_Date ( @Date1 datetime, @Date2 datetime, @Date3 datetime, @Date4 datetime, @Date5 datetime, @Date6 datetime, @Date7 datetime, @Date8 datetime, @Date9 datetime, @Date10 datetime ) RETURNS TABLE AS RETURN ( SELECT Max(DateValue) Max_Date, Min(DateValue) Min_Date FROM ( VALUES (@Date1), (@Date2), (@Date3), (@Date4), (@Date5), (@Date6), (@Date7), (@Date8), (@Date9), (@Date10) ) AS Dates(DateValue) ) 
 DECLARE @TableName TABLE (Number INT, Date1 DATETIME, Date2 DATETIME, Date3 DATETIME, Cost MONEY) INSERT INTO @TableName SELECT 1, '20000101', '20010101','20020101',100 UNION ALL SELECT 2, '20000101', '19900101','19980101',99 SELECT Number, Cost , (SELECT MAX([Date]) FROM (SELECT Date1 AS [Date] UNION ALL SELECT Date2 UNION ALL SELECT Date3 ) D ) [Most Recent Date] FROM @TableName 

Para o T-SQL (MSSQL 2008+)

 SELECT (SELECT MAX(MyMaxName) FROM ( VALUES (MAX(iSortCode)), (MAX(Field2)) ) MyAlias(MyMaxName) ) FROM MyTable1 
 SELECT CASE WHEN Date1 >= Date2 AND Date1 >= Date3 THEN Date1 WHEN Date2 >= Date3 THEN Date2 ELSE Date3 END AS MostRecentDate 

Isso é um pouco mais fácil de escrever e ignora as etapas de avaliação, pois a instrução case é avaliada em ordem.

Infelizmente a resposta de Lasse , embora aparentemente óbvia, tem uma falha crucial. Não consegue lidar com valores NULL. Qualquer valor único NULL resulta em Date1 sendo retornado. Infelizmente, qualquer tentativa de consertar esse problema tende a ficar extremamente confusa e não se adapta muito bem a 4 ou mais valores.

primeira resposta do database parecia (e é) bom. No entanto, não ficou claro se a resposta seria facilmente extrapolar para 3 valores de uma junit de várias tabelas em vez dos 3 valores mais simples de uma única tabela. Eu queria evitar transformar essa consulta em uma subconsulta apenas para obter o máximo de 3 colunas, também eu tinha certeza que a excelente ideia do database poderia ser melhorada um pouco.

Então, sem mais delongas, aqui está minha solução (derivada da ideia do database).
Ele usa junções cruzadas selecionando constantes para simular o efeito de uma junit de várias tabelas. O importante é notar que todos os aliases necessários são executados corretamente (o que nem sempre é o caso) e isso mantém o padrão bastante simples e bastante escalonável por meio de colunas adicionais.

 DECLARE @v1 INT , @v2 INT , @v3 INT --SET @v1 = 1 --Comment out SET statements to experiment with --various combinations of NULL values SET @v2 = 2 SET @v3 = 3 SELECT ( SELECT MAX(Vals) FROM ( SELECT v1 AS Vals UNION SELECT v2 UNION SELECT v3 ) tmp WHERE Vals IS NOT NULL -- This eliminates NULL warning ) AS MaxVal FROM ( SELECT @v1 AS v1 ) t1 CROSS JOIN ( SELECT @v2 AS v2 ) t2 CROSS JOIN ( SELECT @v3 AS v3 ) t3 

Problema: escolha o valor da taxa mínima dada a uma entidade Requisitos: as taxas da agência podem ser nulas

 [MinRateValue] = CASE WHEN ISNULL(FitchRating.RatingValue, 100) < = ISNULL(MoodyRating.RatingValue, 99) AND ISNULL(FitchRating.RatingValue, 100) < = ISNULL(StandardPoorsRating.RatingValue, 99) THEN FitchgAgency.RatingAgencyName WHEN ISNULL(MoodyRating.RatingValue, 100) < = ISNULL(StandardPoorsRating.RatingValue , 99) THEN MoodyAgency.RatingAgencyName ELSE ISNULL(StandardPoorsRating.RatingValue, 'N/A') END 

Inspirado por esta resposta de Nat

Se você estiver usando o SQL Server 2005, poderá usar o recurso UNPIVOT. Aqui está um exemplo completo:

 create table dates ( number int, date1 datetime, date2 datetime, date3 datetime ) insert into dates values (1, '1/1/2008', '2/4/2008', '3/1/2008') insert into dates values (1, '1/2/2008', '2/3/2008', '3/3/2008') insert into dates values (1, '1/3/2008', '2/2/2008', '3/2/2008') insert into dates values (1, '1/4/2008', '2/1/2008', '3/4/2008') select max(dateMaxes) from ( select (select max(date1) from dates) date1max, (select max(date2) from dates) date2max, (select max(date3) from dates) date3max ) myTable unpivot (dateMaxes For fieldName In (date1max, date2max, date3max)) as tblPivot drop table dates 

Usando CROSS APPLY (para 2005+) ….

 SELECT MostRecentDate FROM SourceTable CROSS APPLY (SELECT MAX(d) MostRecentDate FROM (VALUES (Date1), (Date2), (Date3)) AS a(d)) md 

Do SQL Server 2012, podemos usar o IIF .

  DECLARE @Date1 DATE='2014-07-03'; DECLARE @Date2 DATE='2014-07-04'; DECLARE @Date3 DATE='2014-07-05'; SELECT IIF(@Date1>@Date2, IIF(@Date1>@Date3,@Date1,@Date3), IIF(@Date2>@Date3,@Date2,@Date3)) AS MostRecentDate 

Por favor, tente usar o UNPIVOT :

 SELECT MAX(MaxDt) MaxDt FROM tbl UNPIVOT (MaxDt FOR E IN (Date1, Date2, Date3) )AS unpvt; 

Você poderia criar uma function onde você passaria as datas e então adicionaria a function à instrução de seleção como abaixo. selecione Number, dbo.fxMost_Recent_Date (Date1, Date2, Date3), Cost

 create FUNCTION fxMost_Recent_Date 

(@ Date1 smalldatetime, @ Date2 smalldatetime, @ Date3 smalldatetime) RETURNS smalldatetime COMO INICIAR DECLARAR @Result smalldatetime

 declare @MostRecent smalldatetime set @MostRecent='1/1/1900' if @Date1>@MostRecent begin set @MostRecent=@Date1 end if @Date2>@MostRecent begin set @MostRecent=@Date2 end if @Date3>@MostRecent begin set @MostRecent=@Date3 end RETURN @MostRecent 

FIM

Com base na solução da ScottPletcher de http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/Q_24204894.html , criei um conjunto de funções (por exemplo, GetMaxOfDates3, GetMaxOfDates13) para encontrar o máximo de até 13 valores de data usando UNION ALL. Veja a function T-SQL para obter o máximo de valores da mesma linha. No entanto, não considerei a solução UNPIVOT no momento da gravação dessas funções.

Outra maneira de usar CASE WHEN

 SELECT CASE true WHEN max(row1) >= max(row2) THEN CASE true WHEN max(row1) >= max(row3) THEN max(row1) ELSE max(row3) end ELSE CASE true WHEN max(row2) >= max(row3) THEN max(row2) ELSE max(row3) END END FROM yourTable 

Eu não sei se é em SQL, etc … em M $ ACCESS ajuda existe uma function chamada MAXA(Value1;Value2;...) que é suposto fazer tal.

A esperança pode ajudar alguém.

PD: Os valores podem ser colunas ou calculados, etc.

Aqui está uma boa solução:

 CREATE function [dbo].[inLineMax] (@v1 float,@v2 float,@v3 float,@v4 float) returns float as begin declare @val float set @val = 0 declare @TableVal table (value float ) insert into @TableVal select @v1 insert into @TableVal select @v2 insert into @TableVal select @v3 insert into @TableVal select @v4 select @val= max(value) from @TableVal return @val end