【问题标题】:How can I include null values in a MIN or MAX?如何在 MIN 或 MAX 中包含空值?
【发布时间】:2014-01-22 14:52:44
【问题描述】:

我有一个存储时间跨度数据的表。该表的架构类似于:

ID INT NOT NULL IDENTITY(1,1)   
RecordID INT NOT NULL  
StartDate DATE NOT NULL  
EndDate DATE NULL  

我正在尝试计算每个记录 ID 的开始日期和结束日期,因此最小 StartDate 和最大 EndDate。 StartDate 不可为空,因此我无需担心这一点,但我需要 MAX(EndDate) 来表示这是当前正在运行的时间跨度。

重要的是我保持 EndDate 的 NULL 值并将其视为最大值。

最简单的尝试(如下)无法突出显示 MIN 和 MAX 将忽略 NULLS 的问题(来源:http://technet.microsoft.com/en-us/library/ms179916.aspx)。

SELECT recordid, MIN(startdate), MAX(enddate) FROM tmp GROUP BY recordid

我已经创建了一个 SQL Fiddle 并完成了基本设置。

http://sqlfiddle.com/#!3/b0a75

我怎样才能让 SQL Server 2008 按照我的意愿从 SQLFiddle 中给出的数据中产生以下结果?

RecordId  Start       End  
1         2009-06-19  NULL
2         2012-05-06  NULL
3         2013-01-25  NULL
4         2004-05-06  2009-12-01

【问题讨论】:

  • 不明白 - 如果我运行 SELECT recordid, MIN(startdate), Max(enddate) FROM tmp GROUP BY recordid 则保留 null!
  • - 您在 OP 中显示的结果似乎与 sqlfiddle 架构无关?
  • 查看sqlfiddle.com/#!3/565dc/29 以获取有效的示例数据。如果一个分组只存在一条记录,则 NULL 将被保留,否则它将被任何日期覆盖。
  • @whytheq,你说得对,它没有。这是一个示例结果。
  • 我已更新问题,因此结果对示例数据有效。

标签: sql sql-server tsql


【解决方案1】:

这有点难看,但因为NULLs 对你有特殊意义,这是我能想到的最干净的方式:

SELECT recordid, MIN(startdate),
   CASE WHEN MAX(CASE WHEN enddate IS NULL THEN 1 ELSE 0 END) = 0
        THEN MAX(enddate)
   END
FROM tmp GROUP BY recordid

也就是说,如果任何行有NULL,我们希望强制它成为答案。只有当没有行包含NULL 时,我们才应该返回MIN(或MAX)。

【讨论】:

  • +1 。 . .但是 OP 真的想要最大的结束日期,尽管有 SQL(“所以最小的 StartDate 和最大的 EndDate”)。
  • @GordonLinoff 我实际上想将 NULL 保持为最高值。我已经编辑了问题以澄清,谢谢。
【解决方案2】:

您想要的效果是将 NULL 视为最大可能日期,然后在完成后再次将其替换为 NULL:

SELECT RecordId, MIN(StartDate), NULLIF(MAX(COALESCE(EndDate,'9999-12-31')),'9999-12-31') 
  FROM tmp GROUP BY RecordId

根据您的小提琴,这将返回您在所有条件下指定的确切结果。

【讨论】:

  • 这比 IMO 接受的答案更干净。我使用了一个细微的变化:NULLIF(MAX(COALESCE(EndDate, 'infinity'::timestamp)), 'infinity'::timestamp)
  • 不知道那是什么 dbms,但问题是针对 SQL 服务器的。
  • 请注意,在 8000 年后,我们将需要审查此代码......但是,是的,比公认的答案要清晰得多。
  • 我相信@jbg 的变体是 PostgreSQL 特有的
  • 任何寻找最小日期(即 start_date 或类似日期)的人,MySQL 中支持的最小日期是 1000-01-01 Source
【解决方案3】:

在我的表达式中,count(enddate) 计算enddate 列不为空的行数。 count(*) 表达式计算总行数。 通过比较,您可以轻松判断enddate 列中的任何值是否包含null。如果它们相同,则结果为max(enddate)。否则case 将默认返回null,这也是答案。这是一种非常流行的精确检查方法。

SELECT recordid, 
MIN(startdate), 
case when count(enddate) = count(*) then max(enddate) end
FROM tmp 
GROUP BY recordid

【讨论】:

【解决方案4】:

使用 IsNull

SELECT recordid, MIN(startdate), MAX(IsNull(enddate, Getdate()))
FROM tmp 
GROUP BY recordid

我已将第二条指令中的 MIN 修改为 MAX

【讨论】:

  • 甚至更好,Coalesce,它与 ansi-sql 兼容
  • 不幸的是,我需要将 null 保持为高值。我已经编辑了我的问题,以便更清楚地说明这一点。
  • 问题是询问 enddate 的最大/最小值。我看不到 getdate 如何替换未知表中的最小值或最大值,它可能是日历表。我也看不到返回 getdate 而不是 null 的好处。
  • 样本不正确,在 EndDate 列中声明 MIN 而不是 MAX。
【解决方案5】:

假设对于给定的 RecordID,EndDate 列中只有一条为 null 的记录, 像这样的东西应该会给你想要的输出:

WITH cte1 AS
(
SELECT recordid, MIN(startdate) as min_start , MAX(enddate) as max_end
FROM tmp 
GROUP BY recordid
)

SELECT a.recordid, a.min_start , 
CASE 
   WHEN b.recordid IS  NULL THEN a.max_end
END as max_end
FROM cte1 a
LEFT JOIN tmp b ON (b.recordid = a.recordid AND b.enddate IS NULL)

【讨论】:

  • 这确实有效。看看 Damien_The_Unbeliever 的回答是否有任何性能差异会很有趣。
  • 不是一个很好的答案。正如您提到的,它只需要 1 行空列,其他答案更短且没有先决条件
  • @t-clausen.dk : 1 行的空限制很容易克服 - LEFT JOIN (SELECT DISTINCT recordid WHERE enddate is NULL)b。我不确定我的问题是否正确,所以我没有走那么远......我同意,这不是最好的解决方案,只是想展示一些不同的方法
【解决方案6】:

使用解析函数:

select case when 
    max(field) keep (dense_rank first order by datfin desc nulls first) is null then 1 
    else 0 end as flag 
from MYTABLE;

【讨论】:

    【解决方案7】:

    我尝试使用联合来组合两个查询来格式化您想要的返回:

    SELECT recordid, startdate, enddate FROM tmp Where enddate is null UNION SELECT recordid, MIN(startdate), MAX(enddate) FROM tmp GROUP BY recordid

    但我不知道联盟是否会对性能产生很大影响

    【讨论】:

      猜你喜欢
      • 2016-11-18
      • 2017-06-10
      • 2023-04-10
      • 2019-11-11
      • 1970-01-01
      • 1970-01-01
      • 2021-04-03
      • 1970-01-01
      • 2019-01-09
      相关资源
      最近更新 更多