如何获取连续日期的计数

How to get count of consecutive dates

例如,有些 table 的日期为:

2015-01-01
2015-01-02
2015-01-03
2015-01-06
2015-01-07
2015-01-11

我必须编写 ms sql 查询,它将 return 从 table 中的每个日期开始计算连续日期。所以结果会是这样的:

2015-01-01   1
2015-01-02   2
2015-01-03   3
2015-01-06   1
2015-01-07   2
2015-01-11   1

在我看来,我应该使用LAG和LEAD功能,但现在我什至无法想象这种思维方式。

我假设这个 table:

SELECT * 
INTO #Dates
FROM (VALUES
  (CAST('2015-01-01' AS DATE)),
  (CAST('2015-01-02' AS DATE)),
  (CAST('2015-01-03' AS DATE)),
  (CAST('2015-01-06' AS DATE)),
  (CAST('2015-01-07' AS DATE)),
  (CAST('2015-01-11' AS DATE))) dates(d);

这是一个带有解释的递归解决方案:

WITH
  dates AS (
    SELECT
      d, 
      -- This checks if the current row is the start of a new group by using LAG()
      -- to see if the previous date is adjacent
      CASE datediff(day, d, LAG(d, 1) OVER(ORDER BY d)) 
        WHEN -1 THEN 0 
        ELSE 1 END new_group,
      -- This will be used for recursion
      row_number() OVER(ORDER BY d) rn
    FROM #Dates
  ),
  -- Here, the recursion happens
  groups AS (
    -- We initiate recursion with rows that start new groups, and calculate "GRP"
    -- numbers
    SELECT d, new_group, rn, row_number() OVER(ORDER BY d) grp
    FROM dates
    WHERE new_group = 1
    UNION ALL
    -- We then recurse by the previously calculated "RN" until we hit the next group
    SELECT dates.d, dates.new_group, dates.rn, groups.grp
    FROM dates JOIN groups ON dates.rn = groups.rn + 1
    WHERE dates.new_group != 1
  )
-- Finally, we enumerate rows within each group
SELECT d, row_number() OVER (PARTITION BY grp ORDER BY d)
FROM groups
ORDER BY d

SQLFiddle

你可以使用这个CTE:

;WITH CTE AS (
   SELECT [Date],        
          ROW_NUMBER() OVER(ORDER BY [Date]) AS rn,
          CASE WHEN DATEDIFF(Day, PrevDate, [Date]) IS NULL THEN 0
               WHEN DATEDIFF(Day, PrevDate, [Date]) > 1 THEN 0
               ELSE 1
          END AS flag
   FROM (
      SELECT [Date], LAG([Date]) OVER (ORDER BY [Date]) AS PrevDate
      FROM #Dates ) d
)

产生以下结果:

Date        rn  flag
===================
2015-01-01  1   0
2015-01-02  2   1
2015-01-03  3   1
2015-01-06  4   0
2015-01-07  5   1
2015-01-11  6   0

您现在要做的就是计算 运行 总共 flag 第一个 前面 零值的出现:

;WITH CTE AS (
   ... cte statements here ...
)
SELECT [Date], b.cnt + 1
FROM CTE AS c
OUTER APPLY (
   SELECT TOP 1 COALESCE(rn, 1) AS rn 
   FROM CTE
   WHERE flag = 0 AND rn < c.rn
   ORDER BY rn DESC
) a 
CROSS APPLY (
   SELECT COUNT(*) AS cnt
   FROM CTE 
   WHERE c.flag <> 0 AND rn < c.rn AND rn >= a.rn
) b

OUTER APPLY 计算当前行之前的 第一个 零值标志的 rn 值。 CROSS APPLY 计算当前记录之前的记录数 直到 第一次出现前面的零值标志。

CREATE TABLE #T ( MyDate DATE) ;
INSERT #T VALUES ('2015-01-01'),('2015-01-02'),('2015-01-03'),('2015-01-06'),('2015-01-07'),('2015-01-11')

SELECT 
    RW=ROW_NUMBER() OVER( PARTITION BY GRP  ORDER BY MyDate) ,MyDate
FROM
(
SELECT 
    MyDate, DATEDIFF(Day, '1900-01-01' , MyDate)- ROW_NUMBER() OVER( ORDER BY MyDate ) AS GRP
FROM #T 
) A

DROP TABLE #T;