连续日期的SQL

时间:2014-07-21 16:24:06

标签: sql sql-server

我有两列包含开始日期和结束日期。

我想编写一个sql查询来计算句点的开头,如果它是上个月的延续+一天,如果我休息超过一天,那么我想从一开始就开始那个时期

Ref     Start       End
----------------------------
6931    01/01/12    31/01/12
6931    01/02/12    29/02/12
6931    01/03/12    31/03/12
6931    01/04/12    30/04/12
6931    01/05/12    31/05/12
6931    01/06/12    30/06/12
6931    01/07/12    31/07/12
6931    01/08/12    31/08/12
6931    01/09/12    30/09/12
6931    01/10/12    31/10/12
6931    01/11/12    30/11/12
6931    01/12/12    31/12/12
6931    01/01/13    31/01/13
6931    01/02/13    28/02/13
6931    01/03/13    31/03/13
6931    01/04/13    18/04/13
6931    01/05/13    31/05/13
6931    01/06/13    30/06/13
6931    01/07/13    11/07/13

理想情况下,上述数据集在查询工作时应具有以下值

Ref     Start       End
----------------------------
6931    01/01/12    18/04/13
6931    01/05/13    11/07/13

非常感谢您的帮助,如果上述情况没有任何意义,请提前告知我。

2 个答案:

答案 0 :(得分:0)

诀窍是找到一种方法来识别属于同一时期的日期,然后在每个组上使用Min / Max。

;WITH PeriodEnd AS
(   SELECT      [Ref],[End]
    FROM        #Periods
    WHERE       DAY(DATEADD(D,1,[END])) <> 1 --Get [End] dates that don't land on the last of the month
    UNION
    SELECT      [Ref],MAX([End])
    FROM        #Periods
    GROUP BY    [Ref] --Grab the last [End] for each [Ref], regardless of whether is lands on the last day of the month
),

PeriodGroups AS
(   SELECT      p.[Ref],p.[Start],p.[End],
                COUNT(pe.[End])OVER(ORDER BY p.[Ref],p.[End] DESC) AS Grouper --Assign a unique number to the dates in each period
    FROM        #Periods p
    LEFT JOIN   PeriodEnd pe ON pe.[End] = p.[End]
)

SELECT      [Ref],
            [Start] = MIN([Start]),
            [End] = MAX([End])
FROM        PeriodGroups
GROUP BY    Grouper,[Ref]
ORDER BY    1,2

编辑:基于Kevin Cook的反馈的增强功能。

SQLFiddle

答案 1 :(得分:0)

设置我们的测试数据(我添加了新的6941范围,涵盖4-1到7-30

DECLARE @Periods TABLE
(
    Ref INT,
    StartDate DATE,
    EndDate DATE
)

INSERT INTO @Periods
VALUES
(6931    ,'01/01/2012'    ,'01/31/2012'),
(6931    ,'02/01/2012'    ,'02/29/2012'),
(6931    ,'03/01/2012'    ,'03/31/2012'),
(6931    ,'04/01/2012'    ,'04/30/2012'),
(6931    ,'05/01/2012'    ,'05/31/2012'),
(6931    ,'06/01/2012'    ,'06/30/2012'),
(6931    ,'07/01/2012'    ,'07/31/2012'),
(6931    ,'08/01/2012'    ,'08/31/2012'),
(6931    ,'09/01/2012'    ,'09/30/2012'),
(6931    ,'10/01/2012'    ,'10/31/2012'),
(6931    ,'11/01/2012'    ,'11/30/2012'),
(6931    ,'12/01/2012'    ,'12/31/2012'),
(6931    ,'01/01/2013'    ,'01/31/2013'),
(6931    ,'02/01/2013'    ,'02/28/2013'),
(6931    ,'03/01/2013'    ,'03/31/2013'),
(6931    ,'04/01/2013'    ,'04/18/2013'),
(6931    ,'05/01/2013'    ,'05/31/2013'),
(6941    ,'04/01/2013'    ,'07/30/2013'),
(6931    ,'07/01/2013'    ,'07/11/2013');

这里我们得到我们正在使用的数据的日期范围

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
SELECT @StartDate = MIN(StartDate) FROM @Periods
SELECT @EndDate = MAX(EndDate) FROM @Periods

下一部分创建一个数字表来生成范围的所有日期,然后我们将获取id,行号和日期偏移组我们的数据。

DECLARE @number_of_numbers INT = 100000;

;WITH
a AS (SELECT 1 AS i UNION ALL SELECT 1),
b AS (SELECT 1 AS i FROM a AS x, a AS y),
c AS (SELECT 1 AS i FROM b AS x, b AS y),
d AS (SELECT 1 AS i FROM c AS x, c AS y),
e AS (SELECT 1 AS i FROM d AS x, d AS y),
f AS (SELECT 1 AS i FROM e AS x, e AS y),
numbers AS 
(
    SELECT TOP(@number_of_numbers)
    ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS number
    FROM f
), 
dt AS
(
    SELECT DISTINCT dt.CheckDate, p.Ref FROM
    (
        SELECT dt.CheckDate FROM
        (
            SELECT DATEADD(DAY, n.number, @StartDate - 1) CheckDate
            FROM numbers n
        ) dt
    ) dt
    INNER JOIN @Periods p
        ON dt.CheckDate >= p.StartDate 
        AND dt.CheckDate <= p.EndDate
),
t AS
(
  SELECT 
  dt.CheckDate d,
  dt.Ref,
  ROW_NUMBER() OVER(PARTITION BY Ref ORDER BY CheckDate) i 
  FROM dt
)

SELECT 
    Ref,
    CAST(MIN(d) AS DATE) PeriodStart,
    CAST(MAX(d) AS DATE) PeriodEnd
FROM t
GROUP BY DATEDIFF(DAY,i,d), t.Ref

这是输出:

Ref   PeriodStart PeriodEnd
6931  2012-01-01  2013-04-18
6931  2013-05-01  2013-05-31
6931  2013-07-01  2013-07-11
6941  2013-04-01  2013-07-30