SQL MAX của nhiều cột?


372

Làm thế nào để bạn trả về 1 giá trị trên mỗi hàng của tối đa một số cột:

Tên bảng

[Number, Date1, Date2, Date3, Cost]

Tôi cần phải trả lại một cái gì đó như thế này:

[Number, Most_Recent_Date, Cost]

Truy vấn?

Câu trả lời:


161

Chà, bạn có thể sử dụng câu lệnh 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

[Đối với Microsoft SQL Server 2008 trở lên, bạn có thể xem xét câu trả lời đơn giản hơn của Sven bên dưới.]


11
Nó sẽ không đủ để sử dụng WHEN Date1 > Date2 AND Date1 > Date3 THEN Date1; WHEN Date2 > Date3 THEN Date3; ELSE Date3?
Treb

21
Câu trả lời rõ ràng, nhưng nó không hoạt động với các giá trị NULL và cố gắng khắc phục điều đó rất lộn xộn.
vỡ mộng vào

5
Necro'ing bài đăng cũ hơn này, nhưng bạn có thể gói mỗi ngày vào một COALESCE để xử lý NULL. Một trong những câu lệnh WHEN sau đó sẽ trông giống như: WHEN Date1> = COALESCE (Date2, '') VÀ Date1> = COALESCE (Date3, '') THEN Date3 (làm tương tự cho người khác khi nào)
Bill Sambrone

Đối với những người đến đây tìm kiếm một cách của MySQL, hãy xem @ bajafresh4life trả lời: stackoverflow.com/a/331873/1412157
LucaM

2
BTW, nó trả về Date1 khi Date2 là null ngay cả khi Date3> Date1.
jumxozizi

853

Đây là một giải pháp hay khác cho Maxchức năng sử dụng T-SQL và SQL Server

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

47
Phiên bản SQL phải> = 2008
Daniel

10
Điều này không hoạt động rất tốt với năm 2008 và xử lý NULL. Giải pháp rất hay.
nycdan

10
@Cheburek: Từ giá trị (v), "value" là bí danh cho bảng ảo và "v" là tên của cột ảo của các giá trị ngày.
Jonas Lincoln

2
Điều này thật tuyệt vời. Tôi có thể tìm tài liệu cho bảng ảo Giá trị () này ở đâu?
Tôi khác tôi

33
Ban đầu tôi cũng không hiểu GIÁ TRỊ (v). Nếu bạn muốn hiểu GIÁ TRỊ hãy thử truy vấn này để tạo bảng 1 cột ảo: CHỌN * TỪ (GIÁ TRỊ (1), (5), (1)) dưới dạng listOfValues ​​(cộtName) Và truy vấn này tạo bảng 2 cột ảo: CHỌN * TỪ (GIÁ TRỊ (1,2), (5,3), (1,4)) dưới dạng bảngOfValues ​​(cộtName1, CộtName2) Bây giờ bạn có thể hiểu tại sao truy vấn mẫu đó có giá trị AS (v) trong đó. Truy vấn cuối cùng của tôi trông như thế này: CHỌN Max (currentValues) là Max TỪ (VALUES (12), (25), (35)) AS allCurrents (currentValues) Nó sẽ chọn giá trị tối đa trong trường hợp này là 35.
Jackson

148

Nếu bạn đang sử dụng MySQL, bạn có thể sử dụng

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

41
thẻ là sqlserver
Codewerks

104
Đúng, nhưng vẫn là một câu trả lời rất hữu ích khi mọi người tìm thấy câu hỏi này liên quan đến MySQL.
philfreo

4
Cũng có sẵn trong PostgreSQL từ 8.1 .
Frozen Flame

4
Không xử lý tốt NULL, nhưng nếu bạn kết hợp (col1, 0) xung quanh các giá trị cột của bạn, bạn sẽ nấu ăn bằng gas, hãy xem câu trả lời này stackoverflow.com/questions/9831851/
Stan Quinn

Và những gì về giải pháp này: stackoverflow.com/a/2166693/4824854
Sandburg

64

Có thêm 3 phương thức trong đó UNPIVOT(1) là nhanh nhất cho đến nay, tiếp theo là Mô phỏng không xoay vòng (3) chậm hơn nhiều so với (1) nhưng vẫn nhanh hơn (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

Giải pháp 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

Giải pháp 2 (Truy vấn phụ trên mỗi hàng)

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

Giải pháp 3 (Mô phỏng 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
Đẹp. Tôi không biết về các nhà khai thác PIVOT và UNPIVOT.
Sako73

Bạn có biết phiên bản nào của SQL Server hỗ trợ trục / hủy không?
vỡ mộng vào

1
@CraigYoung SQL Server 2005 với COMPATITALITY_LEVEL được đặt thành 90.
Paul Syfrett

18

Một trong hai mẫu dưới đây sẽ hoạt động:

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

Thứ hai là phần bổ trợ cho câu trả lời của 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 

Câu trả lời đầu tiên là tốt, nhưng có thể được đơn giản hóa đáng kể. Câu trả lời thứ hai không hoạt động với các giá trị NULL. Cố gắng khắc phục vấn đề đó trở nên rất lộn xộn.
vỡ mộng vào

Bạn nên sử dụng UNION ALL và không phải UNION để tránh hoạt động DISTINCT ngụ ý không cần thiết.
JamieSee

17

Đối với 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

Làm việc trong bất kỳ phiên bản SQL nào đối với tôi, giải pháp hay
Kirill

9

Hàm vô hướng gây ra tất cả các loại vấn đề về hiệu năng, vì vậy tốt hơn hết là bọc logic vào Hàm nội tuyến có giá trị nếu có thể. Đây là chức năng tôi đã sử dụng để thay thế một số Hàm do người dùng xác định đã chọn ngày tối thiểu / tối đa từ danh sách tối đa mười ngày. Khi được kiểm tra trên tập dữ liệu 1 triệu hàng của tôi, Hàm vô hướng mất hơn 15 phút trước khi tôi hủy truy vấn, TVF nội tuyến mất 1 phút, cùng thời gian với việc chọn kết quả vào bảng tạm thời. Để sử dụng lệnh này, hãy gọi hàm từ một truy vấn con trong CHỌN hoặc ỨNG DỤNG CROSS.

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 

Điều này hơi dễ viết ra và bỏ qua các bước đánh giá vì tuyên bố trường hợp được đánh giá theo thứ tự.


4
Cẩn thận. Nếu Date2 là NULL, câu trả lời sẽ là Date3; ngay cả khi Date1 lớn hơn.
vỡ mộng vào

4

Thật không may , câu trả lời của Lasse , mặc dù có vẻ rõ ràng, có một lỗ hổng quan trọng. Nó không thể xử lý các giá trị NULL. Bất kỳ giá trị NULL duy nhất nào dẫn đến Date1 được trả về. Thật không may, bất kỳ nỗ lực nào để khắc phục vấn đề đó có xu hướng trở nên cực kỳ lộn xộn và không quy mô thành 4 hoặc nhiều giá trị rất độc đáo.

Câu trả lời đầu tiên của databyss nhìn (và là) tốt. Tuy nhiên, không rõ liệu câu trả lời có dễ dàng ngoại suy thành 3 giá trị từ phép nối nhiều bảng thay vì 3 giá trị đơn giản hơn từ một bảng không. Tôi muốn tránh biến một truy vấn như vậy thành truy vấn phụ chỉ để có tối đa 3 cột, tôi cũng khá chắc chắn rằng ý tưởng tuyệt vời của databyss có thể được làm sạch một chút.

Vì vậy, không có gì khó chịu, đây là giải pháp của tôi (xuất phát từ ý tưởng của databyss).
Nó sử dụng các phép nối chéo chọn các hằng số để mô phỏng hiệu ứng của phép nối nhiều bảng. Điều quan trọng cần lưu ý là tất cả các bí danh cần thiết đều thực hiện chính xác (điều này không phải luôn luôn như vậy) và điều này giữ cho mẫu khá đơn giản và có thể mở rộng khá thông qua các cột bổ sung.

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

Vấn đề: chọn giá trị tỷ lệ tối thiểu được cung cấp cho một thực thể Yêu cầu: Tỷ lệ đại lý có thể là null

[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 

Lấy cảm hứng từ câu trả lời này từ Nat


3

Nếu bạn đang sử dụng SQL Server 2005, bạn có thể sử dụng tính năng UNPIVOT. Dưới đây là một ví dụ hoàn chỉnh:

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
Tôi nghĩ rằng tôi thích ví dụ UNION hơn.
Lance Fisher

"Làm thế nào để bạn trả lại MỘT GIÁ TRỊ
M ROI

3

Sử dụng CROSS ỨNG DỤNG (cho năm 2005 trở lên) ....

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

3

Từ SQL Server 2012, chúng ta có thể sử dụng 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

Khá đẹp, nhưng không xử lý null. Ví dụ:DECLARE @Date1 DATE='2014-08-01'; DECLARE @Date2 DATE=null; DECLARE @Date3 DATE='2014-07-05'; /*this gets returned*/
jumxozizi

Chúng tôi có thể xử lý các null như thế này: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

Vui lòng thử sử dụng UNPIVOT:

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

1

Tôi thích các giải pháp dựa trên trường hợp khi, giả định của tôi là nó sẽ có tác động ít nhất đến việc giảm hiệu suất có thể so với các giải pháp có thể khác như các giải pháp áp dụng chéo, giá trị (), hàm tùy chỉnh, v.v.

Đây là phiên bản case-when xử lý các giá trị null với hầu hết các trường hợp thử nghiệm có thể:

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
;

và kết quả là:

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
trời ơi, cảm ơn ngài Tôi đã dành rất nhiều thời gian để thực hiện công thức quái vật này vẫn còn cho tôi vô giá trị và bây giờ tôi thấy ánh sáng ở cuối đường hầm.
Tối đa

0

Bạn có thể tạo một hàm trong đó bạn vượt qua ngày và sau đó thêm hàm vào câu lệnh chọn như bên dưới. chọn Số, dbo.fxMost_Recent_Date (Ngày 1, Ngày 2, Ngày 3), Chi phí

create FUNCTION  fxMost_Recent_Date 

(@ Date1 smalldatetime, @ Date2 smalldatetime, @ Date3 smalldatetime) TRẢ LẠI thời gian nhỏ NHƯ 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

KẾT THÚC



0

Một cách khác để sử dụng CASE KHI

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

nhập mô tả hình ảnh ở đâyBảng trên là bảng lương nhân viên với mức lương1, tiền lương 2, tiền lương 3, tiền lương4 dưới dạng cột. Câu hỏi dưới đây sẽ trả về giá trị tối đa trong bốn cột

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

Chạy truy vấn trên sẽ cho đầu ra là lớn nhất (10001)

Logic của truy vấn trên là như sau:

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

đầu ra sẽ là 10001


Đây gần như là một bản sao của giải pháp được đăng vào ngày 29 tháng 11 bởi @sven
Luuk

-3

Đây là một giải pháp tốt:

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

Tôi không biết nếu nó có trên SQL, v.v ... trên trợ giúp M $ ACCESS có một chức năng được gọi MAXA(Value1;Value2;...)là phải làm như vậy.

Hy vọng có thể giúp ai đó.

PD: Giá trị có thể là cột hoặc tính toán, v.v.


1
Microsoft Access là một sản phẩm hoàn toàn khác. Bên cạnh đó, bạn có thể tìm được yêu cầu của mình về một chức năng như vậy không? Tôi chưa bao giờ thấy hoặc nghe về điều này trong Access.
deutschZuid

1
MAXAlà một hàm Excel , không phải Access.
Đêm giao thừa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.