我正在寻找一对 SQL 函数(MS SQL 服务器),它们将 return 日期列表的最大值或最小值

I'm looking for a pair of SQL functions (MS SQL Server) that will return the MAX or MIN of a list of dates

我正在寻找一对 SQL 函数,它们将 return 日期列表的最大值和最小值(或者,两个日期的最大值和最小值 - 我然后可以堆叠调用来处理整个列表)。我不能使用 MAX() 和 MIN() 函数(据我所知),因为这将 return 特定列的最大值或最小值。我需要的是特定行的最大或最小列。

这里有一些示例数据来说明我在寻找什么:

当前查询结果:

MyID    Date1       Date2       Date3       Date4
---------------------------------------------------------
ROW1    1/1/2019    4/23/2020   12/4/1980   5/2/2020
ROW2    6/3/2020    1/1/2020    5/3/2021    11/9/1998
ROW3    8/15/1980   7/4/2019    12/1/2030   1/2/2020

想要的查询结果:

MyID    MaxDate     MinDate
---------------------------------
ROW1    12/4/1980   5/2/2020
ROW2    11/9/1998   5/3/2021    
ROW3    8/15/1980   12/1/2030

(这里,我有4个不同的日期列,在我的实际情况下,我需要筛选8个不同的列。)

理想情况下,我希望能够做这样的事情:

SELECT
    MyID,
    MIN(Date1, Date2, Date3, Date4),
    MAX(Date1, Date2, Date3, Date4)
FROM ...

或者,如果有必要,我可以这样做:

SELECT
    MyID,
    MIN(Date1, MIN(Date2, MIN(Date3, Date4))),
    MAX(Date1, MAX(Date2, MAX(Date3, Date4)))
FROM ...

显然,现有的 MIN 和 MAX 函数不能这样工作。有替代功能吗?我可能可以构造某种 CASE 子句来执行此操作,但它看起来会非常丑陋......使用嵌套 IF 语句可能更简洁一些,但仍然非常丑陋。在我 post 这个之后,我会开始弄乱它。我只是希望有一个更优雅、更简洁的解决方案。

标准 SQL 解决方案是 UNION 所有日期

SELECT MyID, MAX(thedate ) AS maxdate, MIN(thedate ) AS mindate
FROM
(
SELECT MyID, Date1 AS thedate FROM table
UNION ALL
SELECT MyID, Date2 AS thedate FROM table
UNION ALL 
SELECT MyID, Date3 AS thedate FROM table
UNION ALL
SELECT MyID, Date4 AS thedate FROM table
) T
GROUP BY MyID

根据您未指定的 RDBMS,可能有更好的具有 window 函数的其他解决方案。但是这个应该适用于任何 RDBMS。

大多数(但不是全部)数据库实际上支持 least()greatest():

select t.*,
       least(date1, date2, date3, date4) as min_date,
       greatest(date1, date2, date3, date4) as max_date
from t;

唯一需要注意的是,如果 any 的值是 NULL,则 return 的值是 NULL。根据您问题中的示例数据,这似乎不是问题。

编辑:

在 SQL 服务器上,您将逆轴旋转并使用 apply:

select t.*, v.*
from t cross apply
     (select max(dte) as max_date, min(dte) as min_date
      from (values (t.date1), (t.date2), (t.date3), (t.date4)) v(dte)
     ) v

在这里回答我自己的问题 - 经过大量挖掘后,我认为目前没有好的解决方案。所以我最终编写了自己的函数来处理这个问题。

以下是这些函数的代码:

CREATE FUNCTION dbo.maxDate(@date1 DATETIME, @date2 DATETIME) RETURNS DATETIME AS
BEGIN
    DECLARE @result DATETIME
    IF @date1 > @date2 SET @result = @date1
    ELSE SET @result = @date2
    RETURN @result
END
go
CREATE FUNCTION dbo.minDate(@date1 DATETIME, @date2 DATETIME) RETURNS DATETIME AS
BEGIN
    DECLARE @result DATETIME
    IF @date1 < @date2 SET @result = @date1
    ELSE SET @result = @date2
    RETURN @result
END
go

这样就可以进行类似于我问题中第二个建议的查询:

SELECT
    MyID,
    dbo.minDate(Date1, dbo.minDate(Date2, dbo.minDate(Date3, Date4))),
    dbo.maxDate(Date1, dbo.maxDate(Date2, dbo.maxDate(Date3, Date4)))
FROM ...

在 SQL 服务器中,least()greatest() 不可用,我建议使用横向连接将列反转为行,然后聚合以提取最小值和最大值:

select t.myid, x.*
from mytable t
cross apply (
    select max(dt) maxdate, min(dt) mindate
    from (values (date1), (date2), (date3)) x(dt)
) x

这比 union 更有效,因为它只扫描 table 一次(而不是每个 union 成员扫描一次)。