SQL Server - 选择具有条件的最新记录

时间:2016-06-02 03:07:59

标签: sql-server select group-by

我有一张这样的桌子。

表:

ID  EnrollDate  ExitDate
1   4/1/16      8/30/16 
2   1/1/16      null
2   1/1/16      7/3/16
3   2/1/16      8/1/16
3   2/1/16      9/1/16
4   1/1/16      12/12/16
4   1/1/16      12/12/16
4   1/1/16      12/12/16
4   1/1/16      null
5   5/1/16      11/12/16
5   5/1/16      11/12/16
5   5/1/16      11/12/16

需要选择具有这些条件的最新记录。

  1. 只有一条记录具有最近的注册日期 - 选择
  2. 两个或多个共享相同的最近注册日期,并且只有一个记录具有NULL退出日期或最近的退出日期 - 选择具有null的记录。如果没有空记录选择具有最近退出日期的记录
  3. 两个或多个具有相同的注册和退出日期 - 如果存在此情况,请不要选择那些记录
  4. 因此上表的预期结果应为:

    ID  EnrollDate  ExitDate
    1   4/1/16      8/30/16 
    2   1/1/16      null
    3   2/1/16      9/1/16
    4   1/1/16      null
    

    我用group by编写了查询。我不知道如何选择条件2和3。

    select t1.*  from table t1
    INNER JOIN(SELECT Id,MAX(EnrollDate) maxentrydate
    FROM table
    GROUP BY Id)t2 ON EnrollDate = t2.maxentrydate and t1.Id=t2.Id 
    

    请告诉我这是最好的方法。

3 个答案:

答案 0 :(得分:4)

使用rank()窗口功能,我认为这是可能的。

这是未经测试的,但它应该有效:

select t.ID, t.EnrollDate, t.ExitDate
  from (select t.*,
               rank() over(
                 partition by ID 
                 order by EnrollDate desc,
                          case when ExitDate is null then 1 else 2 end,
                          ExitDate desc) as rnk
          from tbl t) t
 where t.rnk = 1
 group by t.ID, t.EnrollDate, t.ExitDate
having count(*) = 1

基本思想是rank()窗口函数将对最“最近”的行进行排名,其值为1,我们会在外部查询的where子句中对其进行过滤。

如果多个行具有相同的“最新”数据,则它们将共享相同的1等级,但会被having count(*) = 1子句过滤掉。

答案 1 :(得分:4)

使用ROW_NUMBER加上CASE表达式来获得所需的结果:

WITH Cte AS(
    SELECT t.*,
        ROW_NUMBER() OVER(
            PARTITION BY t.ID
            ORDER BY
                t.EnrollDate DESC,
                CASE WHEN t.ExitDate IS NULL THEN 0 ELSE 1 END,
                t.ExitDate DESC
        ) AS rn
    FROM Tbl t
    INNER JOIN (
        SELECT 
            ID, 
            COUNT(DISTINCT CHECKSUM(EnrollDate, ExitDate)) AS DistinctCnt, -- Count distinct combination of EnrollDate and ExitDate per ID
            COUNT(*) AS RowCnt -- Count number of rows per ID
        FROM Tbl
        GROUP BY ID
    ) a
        ON t.ID = a.ID
    WHERE 
        (a.DistinctCnt = 1 AND a.RowCnt = 1)
        OR a.DistinctCnt  > 1
)
SELECT 
    ID, EnrollDate, ExitDate
FROM Cte c 
WHERE Rn = 1
  • ORDER BY中的ROW_NUMBER子句负责条件2和3。
  • INNER JOINWHERE条款负责1和4。

ONLINE DEMO

答案 2 :(得分:3)

with B as (
select id, enrolldate , 
  exitdate,
  row_number() over (partition by id order by enrolldate desc, case when exitdate is null then 0 else 1 end, exitdate desc) rn
 from ab )
 select b1.id, b1.enrolldate, b1.exitdate from b  b1
 left join b b2
 on b1.rn = b2.rn -1 and
    b1.id = b2.id and
    b1.exitdate = b2.exitdate and
    b1.enrolldate = b2.enrolldate
where b1.rn = 1 and 
     b2.id is nULL

左连接用于满足3)要求。当记录被退回时,我们不想要它。