我正在寻找一对 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
成员扫描一次)。
我正在寻找一对 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
成员扫描一次)。