SQL MAX aus mehreren Spalten?


372

Wie gibt man 1 Wert pro Zeile des Maximums mehrerer Spalten zurück:

Tabellenname

[Number, Date1, Date2, Date3, Cost]

Ich muss so etwas zurückgeben:

[Number, Most_Recent_Date, Cost]

Abfrage?

Antworten:


161

Nun, Sie können die CASE-Anweisung verwenden:

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

[Für Microsoft SQL Server 2008 und höher können Sie die einfachere Antwort von Sven unten in Betracht ziehen.]


11
Wäre es nicht ausreichend zu benutzen WHEN Date1 > Date2 AND Date1 > Date3 THEN Date1; WHEN Date2 > Date3 THEN Date3; ELSE Date3?
Treb

21
Die offensichtliche Antwort, aber es funktioniert nicht mit NULL-Werten, und der Versuch, dies zu beheben, wird sehr chaotisch.
Desillusioniert

5
Necro'ing diesen älteren Beitrag, aber Sie könnten jedes Datum in eine COALESCE einwickeln, um NULL zu behandeln. Eine dieser WHEN-Anweisungen würde dann so aussehen: WHEN Date1> = COALESCE (Date2, '') AND Date1> = COALESCE (Date3, '') THEN Date3 (machen Sie dasselbe für das andere Wann)
Bill Sambrone

Für diejenigen, die hierher gekommen sind, um nach einem MySQL-Weg zu suchen, besuchen Sie die Antwort von @ bajafresh4life: stackoverflow.com/a/331873/1412157
LucaM

2
Übrigens gibt es Date1 zurück, wenn Date2 null ist, auch wenn Date3> Date1.
Jumxozizi

853

Hier ist eine weitere schöne Lösung für die Max Funktionalität mit T-SQL und SQL Server

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

47
SQL-Version muss> = 2008 sein.
Daniel

10
Dies funktioniert sehr gut mit 2008 und behandelt NULL-Werte. Sehr schöne Lösung.
Nycdan

10
@Cheburek: Ab Wert (v) ist "Wert" der Alias ​​für die virtuelle Tabelle und "v" der Name der virtuellen Spalte der Datumswerte.
Jonas Lincoln

2
Das ist brilliant. Wo finde ich die Dokumentation für diese virtuelle Tabelle Value ()?
Mein Anderes Ich

33
Ich habe VALUE (v) anfangs auch nicht verstanden. Wenn Sie VALUE verstehen möchten, versuchen Sie diese Abfrage, die eine virtuelle 1-Spalten-Tabelle erstellt: SELECT * FROM (VALUES (1), (5), (1)) als listOfValues ​​(columnName) Und diese Abfrage, die eine virtuelle 2-Spalten-Tabelle erstellt: SELECT * FROM (VALUES (1,2), (5,3), (1,4)) als tableOfValues ​​(columnName1, ColumnName2) Jetzt können Sie verstehen, warum diese Beispielabfrage den AS-Wert (v) enthält. Meine letzte Abfrage sah folgendermaßen aus: SELECT Max (currentValues) als Max FROM (VALUES (12), (25), (35)) AS allCurrents (currentValues) Es wird der Maximalwert ausgewählt, der in diesem Fall 35 beträgt.
Jackson

148

Wenn Sie MySQL verwenden, können Sie verwenden

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

41
Tag ist sqlserver
Codewerks

104
Richtig, aber immer noch eine sehr hilfreiche Antwort, da die Leute diese Frage in Bezug auf MySQL finden.
Philfreo

4
Auch in PostgreSQL ab 8.1 verfügbar .
Frozen Flame

4
Behandelt NULL nicht gut, aber wenn Sie (col1, 0) um Ihre Spaltenwerte zusammenwachsen, werden Sie mit Gas kochen, siehe diese Antwort stackoverflow.com/questions/9831851/…
Stan Quinn

Und was ist mit dieser Lösung: stackoverflow.com/a/2166693/4824854
Sandburg

64

Es gibt 3 weitere Methoden, bei denen UNPIVOT(1) bei weitem die schnellste ist, gefolgt von Simulated Unpivot (3), das viel langsamer als (1), aber immer noch schneller als (2) ist.

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

Lösung 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

Lösung 2 (Unterabfrage pro Zeile)

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

Lösung 3 (simuliert 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
Nett. Die PIVOT- und UNPIVOT-Operatoren waren mir nicht bekannt.
Sako73

Irgendeine Idee, welche Versionen von SQL Server Pivot / Unpivot unterstützen?
Desillusioniert

1
@CraigYoung SQL Server 2005 mit COMPATIBILITY_LEVEL auf 90 gesetzt.
Paul Syfrett

18

Eines der beiden folgenden Beispiele funktioniert:

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

Die zweite ist eine Ergänzung zu Lassevks Antwort.

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 

Die erste Antwort ist gut, kann aber erheblich vereinfacht werden. Die zweite Antwort funktioniert nicht mit NULL-Werten. Der Versuch, dieses Problem zu beheben, wird sehr chaotisch.
Desillusioniert

Sie sollten UNION ALL und nicht UNION verwenden, um eine unnötige implizite DISTINCT-Operation zu vermeiden.
JamieSee

17

Für 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

Arbeitete in jeder SQL-Version für mich, schöne Lösung
Kirill

9

Skalarfunktionen verursachen alle Arten von Leistungsproblemen. Daher ist es besser, die Logik nach Möglichkeit in eine Inline-Tabellenwertfunktion zu packen. Dies ist die Funktion, mit der ich einige benutzerdefinierte Funktionen ersetzt habe, bei denen die Min / Max-Daten aus einer Liste von bis zu zehn Daten ausgewählt wurden. Beim Testen meines Datensatzes mit 1 Million Zeilen dauerte die Skalarfunktion mehr als 15 Minuten, bevor ich die Abfrage abbrach. Die Inline-TVF dauerte 1 Minute. Dies entspricht der Zeit, die für die Auswahl der Ergebnismenge in einer temporären Tabelle erforderlich ist. Um dies zu verwenden, rufen Sie die Funktion entweder über eine Unterabfrage in SELECT oder über eine CROSS APPLY auf.

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 

Dies ist etwas einfacher zu schreiben und überspringt Bewertungsschritte, da die case-Anweisung der Reihe nach ausgewertet wird.


4
Vorsichtig. Wenn Date2 NULL ist, lautet die Antwort Date3. auch wenn Date1 größer ist.
Desillusioniert

4

Leider hat Massees Antwort , obwohl sie offensichtlich erscheint, einen entscheidenden Fehler. Es kann keine NULL-Werte verarbeiten. Jeder einzelne NULL-Wert führt dazu, dass Date1 zurückgegeben wird. Leider wird jeder Versuch, dieses Problem zu beheben, extrem chaotisch und lässt sich nicht sehr gut auf 4 oder mehr Werte skalieren.

Die erste Antwort von databyss sah (und ist) gut aus. Es war jedoch nicht klar, ob die Antwort leicht auf 3 Werte aus einem Join mit mehreren Tabellen anstatt auf die einfacheren 3 Werte aus einer einzelnen Tabelle extrapoliert werden kann. Ich wollte vermeiden, eine solche Abfrage in eine Unterabfrage umzuwandeln, um maximal 3 Spalten zu erhalten. Außerdem war ich mir ziemlich sicher, dass die hervorragende Idee von databyss ein wenig aufgeräumt werden kann.

Also ohne weiteres, hier ist meine Lösung (abgeleitet von der Idee von databyss).
Es verwendet Cross-Joins, bei denen Konstanten ausgewählt werden, um den Effekt eines Multi-Table-Joins zu simulieren. Es ist wichtig zu beachten, dass alle erforderlichen Aliase korrekt ausgeführt werden (was nicht immer der Fall ist), wodurch das Muster durch zusätzliche Spalten recht einfach und ziemlich skalierbar bleibt.

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

Problem: Wählen Sie den Mindestpreis, der einem Unternehmen zugewiesen wird. Anforderungen: Die Agentursätze können null sein

[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 

Inspiriert von dieser Antwort von Nat


3

Wenn Sie SQL Server 2005 verwenden, können Sie die UNPIVOT-Funktion verwenden. Hier ist ein vollständiges Beispiel:

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
Ich denke, ich mag das UNION-Beispiel besser.
Lance Fisher

"Wie können Sie
EINEN

3

CROSS APPLY verwenden (für 2005+) ....

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

3

Ab SQL Server 2012 können wir IIF verwenden .

 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

Ziemlich nett, behandelt aber keine Nullen. Zum Beispiel:DECLARE @Date1 DATE='2014-08-01'; DECLARE @Date2 DATE=null; DECLARE @Date3 DATE='2014-07-05'; /*this gets returned*/
Jumxozizi

Wir könnten mit Nullen wie diesen umgehen: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

1

Bitte versuchen Sie es mit UNPIVOT:

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

1

Ich bevorzuge Lösungen, die auf dem Fall basieren, in dem ich davon ausgehe, dass sie den geringsten Einfluss auf den möglichen Leistungsabfall haben sollten, verglichen mit anderen möglichen Lösungen wie Cross-Apply, values ​​(), benutzerdefinierten Funktionen usw.

Hier ist die Case-When-Version, die mit den meisten möglichen Testfällen Nullwerte verarbeitet:

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
;

und das Ergebnis ist:

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 Gott, danke, Sir! Ich habe so viel Zeit damit verbracht, diese verdammte Monsterformel zu machen, die mir immer noch Nullen gab, und jetzt sehe ich das Licht am Ende des Tunnels.
Max S.

0

Sie können eine Funktion erstellen, in der Sie die Daten übergeben und die Funktion dann wie unten beschrieben zur select-Anweisung hinzufügen. Wählen Sie Nummer, dbo.fxMost_Recent_Date (Datum1, Datum2, Datum3), Kosten

create FUNCTION  fxMost_Recent_Date 

(@ Date1 smalldatetime, @ Date2 smalldatetime, @ Date3 smalldatetime) RETURNS smalldatetime AS BEGIN 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

ENDE



0

Eine andere Möglichkeit, CASE WHEN zu verwenden

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

Geben Sie hier die Bildbeschreibung einDie obige Tabelle enthält eine Gehaltsliste für Mitarbeiter mit Gehalt1, Gehalt2, Gehalt3, Gehalt4 als Spalten. Die folgende Abfrage gibt den Maximalwert aus vier Spalten zurück

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

Wenn Sie die obige Abfrage ausführen, wird die Ausgabe als größter Wert (10001) ausgegeben.

Die Logik der obigen Abfrage ist wie folgt:

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

Ausgabe wird 10001 sein


Dies ist fast eine Kopie der Lösung, die am 29. Juli 11 von @sven
Luuk

-3

Hier ist eine gute Lösung:

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

Ich weiß nicht, ob es sich um SQL usw. handelt. In der M $ ACCESS-Hilfe gibt es eine Funktion namens MAXA(Value1;Value2;...), die dies tun soll.

Hoffnung kann jemandem helfen.

PD: Werte können Spalten oder berechnete Werte usw. sein.


1
Microsoft Access ist ein völlig anderes Produkt. Können Sie außerdem Ihren Anspruch auf eine solche Funktion geltend machen? Ich habe dies in Access noch nie gesehen oder gehört.
deutschZuid

1
MAXAist eine Excel-Funktion , kein Zugriff.
Felix Eve
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.