SQL MAX di più colonne?


372

Come si restituisce 1 valore per riga del massimo di più colonne:

TableName

[Number, Date1, Date2, Date3, Cost]

Devo restituire qualcosa del genere:

[Number, Most_Recent_Date, Cost]

Query?

Risposte:


161

Bene, puoi usare l'istruzione 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

[Per Microsoft SQL Server 2008 e versioni successive, è possibile prendere in considerazione la risposta più semplice di Sven di seguito.]


11
Non basterebbe usare WHEN Date1 > Date2 AND Date1 > Date3 THEN Date1; WHEN Date2 > Date3 THEN Date3; ELSE Date3?
Treb,

21
La risposta ovvia, ma non funziona con i valori NULL e il tentativo di risolvere ciò diventa molto disordinato.
Disilluso il

5
Annullare questo post più vecchio, ma potresti racchiudere ogni data in una COALESCE per gestire i NULL. Una di quelle dichiarazioni QUANDO assomiglierebbe quindi: QUANDO Data1> = COALESCE (Date2, '') AND Date1> = COALESCE (Date3, '') THEN Date3 (fare lo stesso per l'altro quando è)
Bill Sambrone,

per coloro che sono venuti qui in cerca di un modo di MySQL, date un'occhiata alla risposta @ bajafresh4life: stackoverflow.com/a/331873/1412157
Lucam

2
A proposito, restituisce Date1 quando Date2 è null anche se Date3> Date1.
jumxozizi,

853

Ecco un'altra bella soluzione per la Maxfunzionalità che utilizza T-SQL e SQL Server

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

47
La versione SQL deve essere> = 2008.
Daniel,

10
Funziona molto bene con il 2008 e gestisce i NULL. Soluzione molto bella.
nycdan,

10
@Cheburek: da value (v), "value" è l'alias per la tabella virtuale e "v" è il nome della colonna virtuale dei valori della data.
Jonas Lincoln,

2
È brillante. Dove posso trovare la documentazione per questa tabella virtuale Value ()?
My Other Me

33
Inizialmente non capivo neanche VALUE (v). Se vuoi capire VALUE prova questa query che crea una tabella di 1 colonna virtuale: SELECT * FROM (VALUES (1), (5), (1)) come listOfValues ​​(columnName) E questa query che crea una tabella di 2 colonne virtuale: SELEZIONA * FROM (VALUES (1,2), (5,3), (1,4)) come tableOfValues ​​(columnName1, ColumnName2) Ora puoi capire perché quella query di esempio ha un valore AS (v) in essa. La mia query finale sembrava così: SELEZIONA Max (currentValues) come Max FROM (VALUES (12), (25), (35)) AS allCurrents (currentValues) Sceglierà il valore massimo che in questo caso è 35.
Jackson

148

Se stai usando MySQL, puoi usare

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

41
il tag è sqlserver
Codewerks,

104
Vero, ma comunque una risposta molto utile in quanto le persone trovano questa domanda in riferimento a MySQL.
philfreo,

4
Disponibile anche in PostgreSQL da 8.1 .
Frozen Flame,

4
Non gestisce bene i NULL, ma se si fondono (col1, 0) attorno ai valori della colonna si cucina con gas, vedere questa risposta stackoverflow.com/questions/9831851/…
Stan Quinn,

E che dire di questa soluzione: stackoverflow.com/a/2166693/4824854
Sandburg,

64

Esistono altri 3 metodi in cui UNPIVOT(1) è di gran lunga il più veloce, seguito da Simulated Unpivot (3) che è molto più lento di (1) ma comunque più veloce di (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

Soluzione 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

Soluzione 2 (query secondaria per riga)

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

Soluzione 3 (simulata 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

1
Bello. Non ero a conoscenza degli operatori PIVOT e UNPIVOT.
Sako73,

Qualche idea su quali versioni di SQL Server supportano pivot / unpivot?
Disilluso il

1
@CraigYoung SQL Server 2005 con COMPATIBILITY_LEVEL impostato su 90.
Paul Syfrett,

18

Uno dei due campioni seguenti funzionerà:

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

Il secondo è un componente aggiuntivo alla risposta di 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 

La prima risposta è buona, ma può essere notevolmente semplificata. La seconda risposta non funziona con i valori NULL. Il tentativo di risolvere quel problema diventa molto complicato.
Disilluso il

È necessario utilizzare UNION ALL e non UNION per evitare un'operazione DISTINCT implicita non necessaria.
Jamie Vedi il

17

Per T-SQL (MSSQL 2008+)

SELECT
  (SELECT
     MAX(MyMaxName) 
   FROM ( VALUES 
            (MAX(Field1)), 
            (MAX(Field2)) 
        ) MyAlias(MyMaxName)
  ) 
FROM MyTable1

9
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

Ha funzionato in qualsiasi versione SQL per me, bella soluzione
Kirill

9

La funzione scalare causa tutti i tipi di problemi di prestazioni, quindi è meglio avvolgere la logica in una funzione con valori di tabella in linea, se possibile. Questa è la funzione che ho usato per sostituire alcune funzioni definite dall'utente che hanno selezionato le date Min / Max da un elenco di fino a dieci date. Quando testato sul mio set di dati da 1 milione di righe, la funzione scalare ha impiegato più di 15 minuti prima di interrompere la query, Inline TVF ha impiegato 1 minuto, che è lo stesso tempo impiegato per selezionare il set di risultati in una tabella temporanea. Per utilizzare questa chiamata la funzione da una sottoquery in SELECT o 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)
)

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

Questo è leggermente più facile da scrivere e salta i passaggi di valutazione quando l'istruzione case viene valutata in ordine.


4
Attento. Se Date2 è NULL, la risposta sarà Date3; anche se Date1 è più grande.
Disilluso il

4

Sfortunatamente la risposta di Lasse , sebbene apparentemente ovvia, ha un difetto cruciale. Non può gestire i valori NULL. Qualsiasi valore NULL singolo restituisce Date1. Sfortunatamente, qualsiasi tentativo di risolvere questo problema tende a diventare estremamente caotico e non si adatta a 4 o più valori molto bene.

la prima risposta di databyss sembrava (ed è) buona. Tuttavia, non era chiaro se la risposta potesse estrapolare facilmente a 3 valori da un join a più tabelle anziché ai 3 valori più semplici da una singola tabella. Volevo evitare di trasformare una tale query in una sottoquery solo per ottenere il massimo di 3 colonne, inoltre ero abbastanza sicuro che l'eccellente idea di databyss potesse essere ripulita un po '.

Quindi, senza ulteriori indugi, ecco la mia soluzione (derivata dall'idea di databyss).
Usa i cross-join selezionando le costanti per simulare l'effetto di un join multi-tabella. La cosa importante da notare è che tutti gli alias necessari vengono eseguiti correttamente (cosa che non sempre accade) e questo mantiene il modello abbastanza semplice e abbastanza scalabile attraverso colonne aggiuntive.

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

4

Problema: scegliere il valore di aliquota minima assegnato a un'entità Requisiti: i tassi di agenzia possono essere nulli

[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 

Ispirato da questa risposta di Nat


3

Se si utilizza SQL Server 2005, è possibile utilizzare la funzionalità UNPIVOT. Ecco un esempio 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

1
Penso che mi piaccia di più l'esempio UNION.
Lance Fisher,

"Come si restituisce UN VALORE PER FILE del massimo di più colonne"
Niikola,

3

Utilizzo di CROSS APPLY (per il 2005+) ....

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

3

Da SQL Server 2012 possiamo usare 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

Molto carino, ma non gestisce i null. Ad esempio:DECLARE @Date1 DATE='2014-08-01'; DECLARE @Date2 DATE=null; DECLARE @Date3 DATE='2014-07-05'; /*this gets returned*/
jumxozizi,

Potremmo gestire i null in questo modo:select IIF(@Date1 > @Date2 or @Date2 is null, IIF(@Date1 > @Date3 or @Date3 is null, @Date1, @Date3), IIF(@Date2 > @Date3 or @Date3 is null, @Date2, @Date3)) as MostRecentDate
jumxozizi il

1

Prova a utilizzare UNPIVOT:

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

1

Preferisco soluzioni basate sul caso-quando, la mia ipotesi è che dovrebbe avere il minimo impatto sul possibile calo delle prestazioni rispetto ad altre possibili soluzioni come quelle con cross-apply, valori (), funzioni personalizzate ecc.

Ecco la versione case-when che gestisce valori null con la maggior parte dei casi di test possibili:

SELECT
    CASE 
        WHEN Date1 > coalesce(Date2,'0001-01-01') AND Date1 > coalesce(Date3,'0001-01-01') THEN Date1 
        WHEN Date2 > coalesce(Date3,'0001-01-01') THEN Date2 
        ELSE Date3
    END AS MostRecentDate
    , *
from 
(values
     (  1, cast('2001-01-01' as Date), cast('2002-01-01' as Date), cast('2003-01-01' as Date))
    ,(  2, cast('2001-01-01' as Date), cast('2003-01-01' as Date), cast('2002-01-01' as Date))
    ,(  3, cast('2002-01-01' as Date), cast('2001-01-01' as Date), cast('2003-01-01' as Date))
    ,(  4, cast('2002-01-01' as Date), cast('2003-01-01' as Date), cast('2001-01-01' as Date))
    ,(  5, cast('2003-01-01' as Date), cast('2001-01-01' as Date), cast('2002-01-01' as Date))
    ,(  6, cast('2003-01-01' as Date), cast('2002-01-01' as Date), cast('2001-01-01' as Date))
    ,( 11, cast(NULL         as Date), cast('2002-01-01' as Date), cast('2003-01-01' as Date))
    ,( 12, cast(NULL         as Date), cast('2003-01-01' as Date), cast('2002-01-01' as Date))
    ,( 13, cast('2003-01-01' as Date), cast(NULL         as Date), cast('2002-01-01' as Date))
    ,( 14, cast('2002-01-01' as Date), cast(NULL         as Date), cast('2003-01-01' as Date))
    ,( 15, cast('2003-01-01' as Date), cast('2002-01-01' as Date), cast(NULL         as Date))
    ,( 16, cast('2002-01-01' as Date), cast('2003-01-01' as Date), cast(NULL         as Date))
    ,( 21, cast('2003-01-01' as Date), cast(NULL         as Date), cast(NULL         as Date))
    ,( 22, cast(NULL         as Date), cast('2003-01-01' as Date), cast(NULL         as Date))
    ,( 23, cast(NULL         as Date), cast(NULL         as Date), cast('2003-01-01' as Date))
    ,( 31, cast(NULL         as Date), cast(NULL         as Date), cast(NULL         as Date))

) as demoValues(id, Date1,Date2,Date3)
order by id
;

e il risultato è:

MostRecent    id   Date1      Date2      Date3
2003-01-01    1    2001-01-01 2002-01-01 2003-01-01
2003-01-01    2    2001-01-01 2003-01-01 2002-01-01
2003-01-01    3    2002-01-01 2001-01-01 2002-01-01
2003-01-01    4    2002-01-01 2003-01-01 2001-01-01
2003-01-01    5    2003-01-01 2001-01-01 2002-01-01
2003-01-01    6    2003-01-01 2002-01-01 2001-01-01
2003-01-01    11   NULL       2002-01-01 2003-01-01
2003-01-01    12   NULL       2003-01-01 2002-01-01
2003-01-01    13   2003-01-01 NULL       2002-01-01
2003-01-01    14   2002-01-01 NULL       2003-01-01
2003-01-01    15   2003-01-01 2002-01-01 NULL
2003-01-01    16   2002-01-01 2003-01-01 NULL
2003-01-01    21   2003-01-01 NULL       NULL
2003-01-01    22   NULL       2003-01-01 NULL
2003-01-01    23   NULL       NULL       2003-01-01
NULL          31   NULL       NULL       NULL

1
oh dio, grazie signore! Ho passato così tanto tempo a fare questo diamine di una formula mostruosa che mi dava ancora dei nullità e ora vedo la luce alla fine del tunnel.
Max S.

0

È possibile creare una funzione in cui si passano le date e quindi aggiungere la funzione all'istruzione select come di seguito. selezionare Numero, dbo.fxMost_Recent_Date (Data1, Data2, Data3), Costo

create FUNCTION  fxMost_Recent_Date 

(@ Date1 smalldatetime, @ Date2 smalldatetime, @ Date3 smalldatetime) RITORNA smalldatetime COME INIZIARE DECLARE @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

FINE



0

Un altro modo di usare CASE QUANDO

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

-1

inserisci qui la descrizione dell'immagineSopra la tabella è una tabella di stipendio dei dipendenti con stipendio1, stipendio2, stipendio3, stipendio4 come colonne. La query di seguito restituirà il valore massimo di quattro colonne

select  
 (select Max(salval) from( values (max(salary1)),(max(salary2)),(max(salary3)),(max(Salary4)))alias(salval)) as largest_val
 from EmployeeSalary

L'esecuzione sopra la query fornirà l'output come larger_val (10001)

La logica della query sopra è la seguente:

select Max(salvalue) from(values (10001),(5098),(6070),(7500))alias(salvalue)

l'output sarà 10001


Questa è quasi una copia della soluzione pubblicata il 29 luglio 11 da @sven
Luuk il

-3

ecco una buona soluzione:

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 

-3

Non so se è su SQL, ecc ... nella guida di M $ ACCESS c'è una funzione chiamata MAXA(Value1;Value2;...)che dovrebbe fare questo.

La speranza può aiutare qualcuno.

PD: i valori possono essere colonne o calcolati, ecc.


1
Microsoft Access è un prodotto completamente diverso. Inoltre, sei in grado di ottenere la tua richiesta di tale funzione? Non ho mai visto o sentito parlare di questo in Access.
deutschZuid

1
MAXAè una funzione di Excel , non Access.
Felix Eve,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.