如何计算连续日期

时间:2015-01-31 08:35:18

标签: sql-server date

例如,有一些日期表:

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

我必须编写ms sql查询,它将返回从表中每个日期开始的连续日期的计数。所以结果就像:

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功能,但现在我甚至无法想象思维方式。

3 个答案:

答案 0 :(得分:13)

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;

答案 1 :(得分:1)

您可以使用此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计算当前记录之前的记录数第一次出现的前置零值标记。

答案 2 :(得分:0)

我假设这张桌子:

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