【问题标题】:SQL - Finding the earliest and latest non null value in a groupSQL - 查找组中最早和最新的非空值
【发布时间】:2014-07-21 20:29:20
【问题描述】:

我需要在 Group By 语句中找到列的最早和最新(非 Null)值。

例子:

Bob 在某一天有多个购买记录。我需要在当天的一组记录中找到 Bob 的第一次和最后一次购买。

  • Bob 于 2014 年 1 月 1 日下午 12:00 买了铅笔
  • Bob 没有进行购买,但在 2014 年 1 月 1 日下午 1:00 签到
  • Bob 于 2014 年 1 月 1 日下午 2:00 购买了纸张

所以我有三条记录,一条为空值,表示未进行购买。

我已使用以下查询来检索在某一天有多个员工记录的所有员工记录。

SELECT nameid, 
       Min(CONVERT(NVARCHAR(25), mdatetime, 10)) AS Date, 
       Count(*)                                  AS Duplicates 
       -- <<<Get Earliest Purchase And Latest Purchase From each group Where Not Null>>>
FROM   employee 
GROUP  BY nameid,  CONVERT(NVARCHAR(25), mdatetime, 111) 
HAVING Count(*) > 1 

如果我只是检索最早的日期,使用 MIN(mDateTime) 可能该值将为空。我需要每个分组中最早的非空值和最新的非空值。

感谢您的指导和耐心。

警告:这是我实际编辑的代码的假设再现。场景、命名约定和完整性被缩写以简化和突出问题。

【问题讨论】:

  • 我已经编辑了您的问题,将WHERE 替换为GROUP BY + HAVING 子句(以及其他),因为我认为错误的语法不是您的问题的一部分,同意吗?
  • 当您说“所以我有三条记录,其中一条为空值,表示没有进行购买。”什么字段为空(保存空值的列的名称是什么)?日期时间?
  • 您要计算所有重复的还是只计算购买的?

标签: sql sql-server-2012


【解决方案1】:

归根结底,答案是:

考虑这个构建方案:

create table employee  (nameid varchar(100), mdatetime datetime, purchase varchar(200))
insert into employee values ('Bob', '2014/01/01 12:00pm', 'books')
insert into employee values ('Bob', '2014/01/01 01:00pm', NULL)
insert into employee values ('Bob', '2014/01/01 02:00pm', 'pencil')

将获取第一个和最后一个非空购买并选择购买字段的查询是:

SELECT A.nameid, A.Date, A.MIN_DATE, B.purchase MIN_PURCHASE, A.MAX_DATE, C.PURCHASE MAX_PURCHASE
FROM (
  SELECT nameid, 
   CONVERT(NVARCHAR(25), mdatetime, 10) AS Date, 
   Min(case when purchase is null then null else mDateTime end) MIN_DATE,
   Max(case when purchase is null then null else mDateTime end) MAX_DATE,
   Count(*) AS Duplicates 
 FROM   employee A
 GROUP  BY nameid,  CONVERT(NVARCHAR(25), mdatetime, 10) 
 HAVING Count(*) > 1 
) A
INNER JOIN employee B
  on A.nameid = B.nameid
  and A.MIN_DATE = B.mdatetime
INNER JOIN employee C
  on A.nameid = C.nameid
  and A.MAX_DATE = C.mdatetime

SQL Fiddle

【讨论】:

  • 我需要最早和最新的 NOT NULL 购买。这不会简单地给我结果中的第一个 NOT NULL 购买吗?购买列包含一个字符串,例如“铅笔”或“纸”。第一条记录有可能只是签到并包含 NULL for purchase。
  • cmets 后更改。有两个查询将为您提供不为空的第一个和最后一个日期。它们之间的差异就是您所计算的。您想每天计算所有重复项还是只计算非空项?
  • 此集合中还有其他字段正在被 SUM()。如果我添加 WHERE 购买不为空。我会丢失准确的总数。
  • 然后使用没有where子句的第二个查询
  • 好的,请耐心等待。您可以更改答案以返回 Min(Date) 和 Max(Date) 的购买列吗?
【解决方案2】:
SELECT nameid,
       CONVERT(NVARCHAR(25), mdatetime, 10) AS dt,
       min(case when purchase is not null then mdatetime else null end) as first_purch,
       max(case when purchase is not null then mdatetime else null end) as last_purch
  FROM employee
 GROUP BY nameid, CONVERT(NVARCHAR(25), mdatetime, 10)
having sum(case when purchase is not null then 1 else 0 end) > 1

如果您还想拥有已购买的商品(第一个和最后一个),您可以运行以下命令:

with sub as(
SELECT nameid,
       CONVERT(NVARCHAR(25), mdatetime, 10) AS dt,
       min(case when purchase is not null then mdatetime else null end) as first_purch,
       max(case when purchase is not null then mdatetime else null end) as last_purch
  FROM employee
 GROUP BY nameid, CONVERT(NVARCHAR(25), mdatetime, 10)
having sum(case when purchase is not null then 1 else 0 end) > 1
)
select  s.nameid,
        s.dt,
        s.first_purch,
        f.purchase as first_purch_item,
        s.last_purch,
        l.purchase as last_purch_item
from sub s join employee f on s.nameid = f.nameid and s.first_purch = f.mdatetime
           join employee l on s.nameid = l.nameid and s.last_purch = l.mdatetime

【讨论】:

  • 不幸的是,即使没有购买,mDateTime 列也包含一个值。
  • 对,但是您说如果没有购买,则其中一个字段为空。那是哪个领域?
  • 好的,所以我将其更改为“购买不为空”。这对你有用吗?
  • 我在每个组上使用 SUM() 来汇总其他字段。我不能使用 where Purchase is Not NULL 否则我会丢失这些组的准确总数。
  • @CCorock 除了将该列添加到选择列表之外,还有更多内容(但是是的,我会的)但是您能确认数据至少是正确的吗?如果是,我将其更改为显示与第一个和最后一个关联的购买字段值
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-10
  • 1970-01-01
  • 2022-06-16
  • 1970-01-01
  • 2013-11-24
  • 2021-06-08
  • 1970-01-01
相关资源
最近更新 更多