SQL MAX dari banyak kolom?

372

Bagaimana Anda mengembalikan 1 nilai per baris maks dari beberapa kolom:

TableName

[Number, Date1, Date2, Date3, Cost]

Saya perlu mengembalikan sesuatu seperti ini:

[Number, Most_Recent_Date, Cost]

Pertanyaan?

BenB
sumber

Jawaban:

161

Nah, Anda bisa menggunakan pernyataan 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

[Untuk Microsoft SQL Server 2008 dan di atasnya, Anda dapat mempertimbangkan jawaban sederhana Sven di bawah ini.]

Lasse V. Karlsen
sumber
11
Bukankah itu cukup untuk digunakan WHEN Date1 > Date2 AND Date1 > Date3 THEN Date1; WHEN Date2 > Date3 THEN Date3; ELSE Date3?
Treb
21
Jawaban yang jelas, tetapi tidak bekerja dengan nilai-nilai NULL, dan berusaha untuk memperbaikinya menjadi sangat berantakan.
Kecewa
5
Necroing postingan lama ini, tetapi Anda bisa membungkus setiap tanggal menjadi COALESCE untuk menangani NULL's. Salah satu pernyataan KAPAN itu kemudian akan terlihat seperti: KAPAN Date1> = COALESCE (Date2, '') AND Date1> = COALESCE (Date3, '') THEN Date3 (lakukan hal yang sama untuk yang lain)
Bill Sambrone
bagi mereka yang datang ke sini mencari cara MySQL, lihat ke @ bajafresh4life balasan: stackoverflow.com/a/331873/1412157
LucaM
2
BTW, itu mengembalikan Date1 ketika Date2 adalah null bahkan jika Date3> Date1.
jumxozizi
853

Berikut ini adalah solusi bagus untuk Maxfungsionalitas menggunakan T-SQL dan SQL Server

SELECT [Other Fields],
  (SELECT Max(v) 
   FROM (VALUES (date1), (date2), (date3),...) AS value(v)) as [MaxDate]
FROM [YourTableName]
Sven
sumber
47
Versi SQL harus> = 2008.
Daniel
10
Ini berfungsi dengan sangat baik pada 2008 dan menangani NULLs. Solusi yang sangat bagus
nycdan
10
@ Cheburek: Dari nilai (v), "nilai" adalah alias untuk tabel virtual dan "v" adalah nama kolom virtual dari nilai tanggal.
Jonas Lincoln
2
Ini brilian. Di mana saya dapat menemukan dokumentasi untuk tabel virtual Value () ini?
My Other Me
33
Awalnya saya juga tidak mengerti VALUE (v). Jika Anda ingin memahami VALUE coba kueri ini yang membuat tabel 1 kolom virtual: PILIH * DARI (VALUES (1), (5), (1)) sebagai listOfValues ​​(columnName) Dan kueri ini yang membuat tabel 2 kolom virtual: SELECT * FROM (VALUES (1,2), (5,3), (1,4)) sebagai tableOfValues ​​(columnName1, ColumnName2) Sekarang Anda dapat memahami mengapa kueri sampel memiliki nilai AS (v) di dalamnya. Kueri terakhir saya terlihat seperti ini: SELECT Max (currentValues) sebagai Max FROM (VALUES (12), (25), (35)) SEBAGAI semua Arus (currentValues) Ini akan memilih nilai maks yang dalam kasus ini adalah 35.
Jackson
148

Jika Anda menggunakan MySQL, Anda bisa menggunakan

SELECT GREATEST(col1, col2 ...) FROM table
bajafresh4life
sumber
41
tag adalah sqlserver
Codewerks
104
Benar, tetapi masih merupakan jawaban yang sangat membantu karena orang menemukan pertanyaan ini mengacu pada MySQL.
philfreo
4
Juga tersedia di PostgreSQL dari 8.1 .
Api Beku
4
Tidak menangani sumur NULL, tetapi jika Anda menyatu (col1, 0) di sekitar nilai kolom Anda, Anda akan memasak dengan gas lihat jawaban ini stackoverflow.com/questions/9831851/…
Stan Quinn
Dan bagaimana dengan solusi ini: stackoverflow.com/a/2166693/4824854
Sandburg
64

Ada 3 metode lagi di mana UNPIVOT(1) adalah yang tercepat sejauh ini, diikuti oleh Simulated Unpivot (3) yang jauh lebih lambat daripada (1) tetapi masih lebih cepat dari (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

Solusi 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

Solusi 2 (Sub query per baris)

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

Solusi 3 (Simulasi 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
Niikola
sumber
1
Bagus. Saya tidak mengetahui operator PIVOT dan UNPIVOT.
Sako73
Adakah versi SQL Server mana yang mendukung pivot / unpivot?
Disillusioned
1
@CraigYoung SQL Server 2005 dengan COMPATIBILITY_LEVEL diatur ke 90.
Paul Syfrett
18

Salah satu dari dua sampel di bawah ini akan berfungsi:

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

Yang kedua adalah add-on untuk jawaban 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 
databyss
sumber
Jawaban pertama adalah baik, tetapi dapat secara signifikan disederhanakan. Jawaban kedua tidak bekerja dengan nilai NULL. Mencoba untuk memperbaiki masalah itu menjadi sangat berantakan.
Kecewa
Anda harus menggunakan UNION ALL dan bukan UNION untuk menghindari operasi DISTINCT tersirat yang tidak perlu.
JamieLihat
17

Untuk T-SQL (MSSQL 2008+)

SELECT
  (SELECT
     MAX(MyMaxName) 
   FROM ( VALUES 
            (MAX(Field1)), 
            (MAX(Field2)) 
        ) MyAlias(MyMaxName)
  ) 
FROM MyTable1
doker
sumber
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
Martin Smith
sumber
Bekerja dalam versi SQL apa pun untuk saya, solusi yang bagus
Kirill
9

Fungsi skalar menyebabkan segala macam masalah kinerja, jadi lebih baik untuk membungkus logika menjadi Fungsi Inline Table Valued jika memungkinkan. Ini adalah fungsi yang saya gunakan untuk mengganti beberapa Fungsi yang Ditentukan Pengguna yang memilih tanggal Min / Max dari daftar hingga sepuluh tanggal. Ketika diuji pada dataset saya dari 1 Juta baris, Fungsi Skalar memakan waktu lebih dari 15 menit sebelum saya membunuh permintaan, Inline TVF membutuhkan 1 menit yang merupakan jumlah waktu yang sama dengan memilih resultset ke dalam tabel sementara. Untuk menggunakan fungsi panggilan ini baik dari subquery di SELECT atau 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)
)
MartinC
sumber
5
SELECT 
    CASE 
        WHEN Date1 >= Date2 AND Date1 >= Date3 THEN Date1 
        WHEN Date2 >= Date3 THEN Date2 
        ELSE Date3
    END AS MostRecentDate 

Ini sedikit lebih mudah untuk dituliskan dan dilewati langkah-langkah evaluasi karena pernyataan kasus dievaluasi secara berurutan.

Nat
sumber
4
Cermat. Jika Date2 NULL, jawabannya adalah Date3; bahkan jika Date1 lebih besar.
Kecewa
4

Sayangnya jawaban Lasse , meskipun tampak jelas, memiliki kelemahan yang sangat penting. Itu tidak bisa menangani nilai NULL. Nilai NULL tunggal menghasilkan Date1 yang dikembalikan. Sayangnya setiap upaya untuk memperbaiki masalah itu cenderung menjadi sangat berantakan dan tidak skala ke 4 atau lebih nilai dengan sangat baik.

jawaban pertama databyss tampak (dan) bagus. Namun, tidak jelas apakah jawabannya akan dengan mudah mengekstrapolasi ke 3 nilai dari multi-tabel bergabung bukan 3 nilai sederhana dari satu tabel. Saya ingin menghindari mengubah query seperti itu menjadi sub-query hanya untuk mendapatkan maks 3 kolom, juga saya cukup yakin ide bagus databyss bisa dibersihkan sedikit.

Jadi tanpa basa-basi lagi, inilah solusi saya (berasal dari ide databyss).
Ini menggunakan cross-joins memilih konstanta untuk mensimulasikan efek multi-table join. Yang penting untuk dicatat adalah bahwa semua alias yang diperlukan dijalankan dengan benar (yang tidak selalu terjadi) dan ini membuat polanya cukup sederhana dan cukup skalabel melalui kolom tambahan.

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
Kecewa
sumber
4

Masalah: pilih nilai tarif minimum yang diberikan ke entitas Persyaratan: Tarif agensi bisa nol

[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 

Terinspirasi oleh jawaban ini dari Nat

Luis Miguel Rosa
sumber
3

Jika Anda menggunakan SQL Server 2005, Anda dapat menggunakan fitur UNPIVOT. Ini adalah contoh lengkapnya:

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
Lance Fisher
sumber
1
Saya pikir saya lebih suka contoh UNION.
Lance Fisher
"Bagaimana Anda mengembalikan SATU NILAI PER ROW dari maks beberapa kolom"
Niikola
3

Menggunakan CROSS APPLY (untuk 2005+) ....

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

Dari SQL Server 2012 kita bisa menggunakan 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
abdulbasit
sumber
Cukup bagus, tetapi tidak menangani nulls. Misalnya:DECLARE @Date1 DATE='2014-08-01'; DECLARE @Date2 DATE=null; DECLARE @Date3 DATE='2014-07-05'; /*this gets returned*/
jumxozizi
Kita dapat menangani nulls seperti ini: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

Silakan coba menggunakan UNPIVOT:

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

Saya lebih suka solusi berdasarkan kasus-kapan, asumsi saya adalah bahwa itu harus memiliki dampak paling kecil pada penurunan kinerja yang mungkin dibandingkan dengan solusi lain yang mungkin seperti yang berlaku silang, nilai (), fungsi kustom dll.

Ini adalah versi case-when yang menangani nilai null dengan sebagian besar kasus uji yang mungkin:

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
;

dan hasilnya adalah:

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
Robert Lujo
sumber
1
oh tuhan, terima kasih tuan! Saya menghabiskan begitu banyak waktu melakukan sih ini formula monster yang masih memberi saya nol dan sekarang saya melihat cahaya di ujung terowongan.
Max S.
0

Anda dapat membuat fungsi di mana Anda melewati tanggal dan kemudian menambahkan fungsi ke pernyataan pilih seperti di bawah ini. pilih Nomor, dbo.fxMost_Recent_Date (Date1, Date2, Date3), Biaya

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

AKHIR

DrYodo
sumber
0

Berdasarkan solusi ScottPletcher dari http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/Q_24204894.html Saya telah membuat serangkaian fungsi (mis. GetMaxOfDates3, GetMaxOfDates13) untuk menemukan maks hingga 13 Nilai tanggal menggunakan UNION ALL. Lihat fungsi T-SQL untuk mendapatkan nilai maksimum dari baris yang sama. Namun saya belum mempertimbangkan solusi UNPIVOT pada saat penulisan fungsi-fungsi ini.

Michael Freidgeim
sumber
0

Cara lain untuk menggunakan KASUS KETIKA

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
MABell
sumber
-1

masukkan deskripsi gambar di siniTabel di atas adalah tabel gaji karyawan dengan gaji1, gaji2, gaji3, gaji4 sebagai kolom. Pertanyaan di bawah ini akan mengembalikan nilai maksimal dari empat kolom

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

Menjalankan kueri di atas akan memberikan output sebagai terbesar_val (10001)

Logika kueri di atas adalah sebagai berikut:

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

output akan menjadi 10001

Brijesh Ray
sumber
Ini hampir merupakan salinan dari solusi yang diposting di 29 jul '11 oleh @sven
Luuk
-3

ini solusi yang bagus:

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 
danvasiloiu
sumber
-3

Saya tidak tahu apakah itu di SQL, dll ... di M $ ACCESS membantu ada fungsi yang disebut MAXA(Value1;Value2;...)yang seharusnya dilakukan.

Semoga bisa membantu seseorang.

PD: Nilai bisa berupa kolom atau yang dihitung, dll.

claudio
sumber
1
Microsoft Access adalah produk yang sama sekali berbeda. Selain itu, apakah Anda dapat mengambil klaim dari fungsi tersebut? Saya belum pernah melihat atau mendengar tentang ini di Access.
deutschZuid
1
MAXAadalah fungsi Excel , bukan Access.
Felix Eve