簡體   English   中英

SQL MAX 多列?

[英]SQL MAX of multiple columns?

如何在幾列的最大值中每行返回 1 個值:

表名

[Number, Date1, Date2, Date3, Cost]

我需要返回這樣的東西:

[Number, Most_Recent_Date, Cost]

詢問?

這是使用 T-SQL 和 SQL Server 的Max功能的另一個不錯的解決方案

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

Values 是表值構造函數

"指定要構造到表中的一組行值表達式。Transact-SQL 表值構造函數允許在單個 DML 語句中指定多行數據。表值構造函數可以指定為INSERT ... VALUES 語句,或作為 MERGE 語句的 USING 子句或 FROM 子句中的派生表。”

這是一個古老的答案,並且在很多方面都被打破了。

請參閱https://stackoverflow.com/a/6871572/194653 ,它有更多的贊成票,可與 sql server 2008+ 一起使用並處理空值等。

原始但有問題的答案

好吧,您可以使用 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

如果您使用的是 MySQL,則可以使用

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

還有 3 種方法,其中UNPIVOT (1) 是迄今為止最快的,其次是 Simulated Unpivot (3),它比 (1) 慢得多但仍然比 (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

解決方案 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

解決方案2(每行子查詢)

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

解決方案 3(模擬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

以下兩個示例中的任何一個都可以使用:

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

第二個是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 

對於 T-SQL (MSSQL 2008+)

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

標量函數會導致各種性能問題,因此如果可能,最好將邏輯包裝到內聯表值函數中。 這是我用來替換一些用戶定義函數的函數,這些函數從最多十個日期的列表中選擇最小/最大日期。 在我的 100 萬行數據集上進行測試時,標量函數在我終止查詢之前花費了 15 多分鍾,而內聯 TVF 花費了 1 分鍾,這與將結果集選擇到臨時表中的時間相同。 要使用此調用,請從 SELECT 或 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
SELECT 
    CASE 
        WHEN Date1 >= Date2 AND Date1 >= Date3 THEN Date1 
        WHEN Date2 >= Date3 THEN Date2 
        ELSE Date3
    END AS MostRecentDate 

由於 case 語句是按順序計算的,所以寫出來會稍微容易一些,並且會跳過計算步驟。

不幸的是, Lasse 的回答雖然看起來很明顯,但有一個關鍵的缺陷。 它無法處理 NULL 值。 任何單個 NULL 值都會導致 Date1 被返回。 不幸的是,任何解決該問題的嘗試都會變得非常混亂,並且不能很好地擴展到 4 個或更多值。

databyss 的第一個答案看起來(而且現在)很好。 但是,不清楚該答案是否可以輕松地從多表連接中推斷出 3 個值,而不是從單個表中推斷出更簡單的 3 個值。 我想避免將這樣的查詢變成子查詢,只是為了獲得最多 3 列,而且我很確定 databyss 的好主意可以稍微清理一下。

所以不用多說,這是我的解決方案(源自databyss的想法)。
它使用交叉連接選擇常量來模擬多表連接的效果。 需要注意的重要一點是,所有必要的別名都正確執行(情況並非總是如此),這使模式非常簡單,並且通過附加列具有相當的可擴展性。

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

問題:選擇給予實體的最低費率值要求:代理費率可以為空

[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 

靈感來自Nat這個答案

使用 CROSS APPLY(適用於 2005+)....

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

從 SQL Server 2012 開始,我們可以使用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

如果您使用的是 SQL Server 2005,則可以使用 UNPIVOT 功能。 這是一個完整的例子:

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

請嘗試使用UNPIVOT

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

我更喜歡基於 case-when 的解決方案,我的假設是,與其他可能的解決方案(例如交叉應用、values()、自定義函數等)相比,它對可能的性能下降的影響應該最小。

這是使用大多數可能的測試用例處理空值的版本:

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
;

結果是:

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

基於來自http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/Q_24204894.htmlScottPletcher的解決方案,我創建了一組函數(例如 GetMaxOfDates3 、 GetMaxOfDates13 )來查找最大值使用 UNION ALL 最多 13 個日期值。 請參閱T-SQL 函數以從同一行獲取最大值但是在編寫這些函數時我還沒有考慮 UNPIVOT 解決方案

CREATE FUNCTION GetMaxOfDates13 (
@value01 DateTime = NULL,  
@value02 DateTime = NULL,
@value03 DateTime = NULL,
@value04 DateTime = NULL,
@value05 DateTime = NULL,
@value06 DateTime = NULL,
@value07 DateTime = NULL,
@value08 DateTime = NULL,
@value09 DateTime = NULL,
@value10 DateTime = NULL,
@value11 DateTime = NULL,
@value12 DateTime = NULL,
@value13 DateTime = NULL
)
RETURNS DateTime
AS
BEGIN
RETURN (
SELECT TOP 1 value
FROM (
SELECT @value01 AS value UNION ALL
SELECT @value02 UNION ALL
SELECT @value03 UNION ALL
SELECT @value04 UNION ALL
SELECT @value05 UNION ALL
SELECT @value06 UNION ALL
SELECT @value07 UNION ALL
SELECT @value08 UNION ALL
SELECT @value09 UNION ALL
SELECT @value10 UNION ALL
SELECT @value11 UNION ALL
SELECT @value12 UNION ALL
SELECT @value13
) AS [values]
ORDER BY value DESC    
)
END –FUNCTION
GO
CREATE FUNCTION GetMaxOfDates3 (
@value01 DateTime = NULL,  
@value02 DateTime = NULL,
@value03 DateTime = NULL
)
RETURNS DateTime
AS
BEGIN
RETURN dbo.GetMaxOfDates13(@value01,@value02,@value03,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)
END –FUNCTION

最后,針對以下內容:

  • SQL 服務器 2022 (16.x) 預覽
  • Azure SQL 數據庫
  • Azure SQL 托管實例

我們也可以使用GREATEST 與其他 T-SQL 函數類似,這里有幾個重要的注意事項:

  • 如果所有 arguments 具有相同的數據類型並且支持該類型進行比較,則 GREATEST 將返回該類型;
  • 否則,該函數將在比較之前將所有 arguments 隱式轉換為最高優先級的數據類型,並使用該類型作為返回類型;
  • 如果一個或多個 arguments 不是 NULL,則 NULL ZDBC11CAA5BDA99F77E6FB4DABD882ED 將被忽略; 如果所有 arguments 都是 NULL,則 GREATEST 將返回 NULL;

GREATEST 中不支持以下類型進行比較:varchar(max)、varbinary(max) 或 nvarchar(max) 超過 8,000 字節、cursor、geometry、geography、image、非字節排序的用戶定義類型、ntext、table 、文本和 xml。

您可以創建一個函數,在其中傳遞日期,然后將該函數添加到 select 語句中,如下所示。 選擇數字、dbo.fxMost_Recent_Date(Date1,Date2,Date3)、成本

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

結尾

另一種使用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

如果我想獲取最大的ID,則有一個名為TblItem的表。 我使用以下代碼。

select MAX(Id) from TblItem

我的解決方案也可以處理空值比較。 可以通過編寫單個查詢來簡化它,但為了解釋,我使用的是 CTE。 這個想法是在步驟 1 中將比較從 3 個數字減少到 2 個數字,然后在步驟 2 中將比較從 2 個數字減少到 1 個數字。

with x1 as
(
  select 1 as N1, null as N2, 3 as N3
  union
  select 1 as N1, null as N2, null as N3
  union
  select null as N1, null as N2, null as N3
)
,x2 as
(
select 
N1,N2,N3,
IIF(Isnull(N1,0)>=Isnull(N2,0),N1,N2) as max1,
IIF(Isnull(N2,0)>=Isnull(N3,0),N2,N3) as max2
from x1
)
,x3 as
(
 select N1,N2,N3,max1,max2,
 IIF(IsNull(max1,0)>=IsNull(max2,0),max1,max2) as MaxNo
 from x2
)
select * from x3

輸出:

在此處輸入圖片說明

這是一個很好的解決方案:

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 

我不知道它是否在 SQL 等上...在 M$ACCESS 幫助中有一個名為MAXA(Value1;Value2;...)函數應該這樣做。

希望可以幫助某人。

PD:值可以是列或計算值等。

在此處輸入圖片說明 上表是一個員工工資表,以salary1、salary2、salary3、salary4為列。下面的查詢將返回四列中的最大值

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

運行上面的查詢將輸出為 large_val(10001)

上述查詢的邏輯如下:

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

輸出將是 10001

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM