【问题标题】:Recursive CTE to split date range递归 CTE 到拆分日期范围
【发布时间】:2015-09-23 17:39:21
【问题描述】:

我需要从成员表中提取和拆分数据。 我想拆分范围以每年获得一条线。

DateFrom 和 dateTo 可以是一年中的任何一天,但是当日期被拆分时,我们假设一行在 12 月 31 日结束,而新的一行从 1 月 1 日开始

这是数据的外观

membershipId - groupId - ClientId - DateFrom - DateTo
2707    20008   1579    1997-01-01 00:00:00.000 1997-12-31 00:00:00.000
20989   20008   1579    1999-01-01 00:00:00.000 2004-12-31 00:00:00.000
39874   20298   1579    2005-01-01 00:00:00.000 2008-12-31 00:00:00.000
50295   21661   1579    2009-01-01 00:00:00.000 2009-12-31 00:00:00.000
50988   20399   1579    2010-01-01 00:00:00.000 2010-12-31 00:00:00.000
52378   21661   1579    2011-01-01 00:00:00.000 2013-12-31 00:00:00.000
57274   21660   1579    2014-01-01 00:00:00.000 3000-01-01 00:00:00.000

预期结果是:(每个范围拆分)

2707    20008   1579    1997-01-01 00:00:00.000 1997-12-31 00:00:00.000
20989   20008   1579    1999-01-01 00:00:00.000 1999-12-31 00:00:00.000
20989   20008   1579    2000-01-01 00:00:00.000 2000-12-31 00:00:00.000
20989   20008   1579    2001-01-01 00:00:00.000 2001-12-31 00:00:00.000
20989   20008   1579    2002-01-01 00:00:00.000 2002-12-31 00:00:00.000
20989   20008   1579    2003-01-01 00:00:00.000 2003-12-31 00:00:00.000
20989   20008   1579    2004-01-01 00:00:00.000 2004-12-31 00:00:00.000
50295   21661   1579    2009-01-01 00:00:00.000 2009-12-31 00:00:00.000
50988   20399   1579    2010-01-01 00:00:00.000 2010-12-31 00:00:00.000
52378   21661   1579    2011-01-01 00:00:00.000 2011-12-31 00:00:00.000
52378   21661   1579    2012-01-01 00:00:00.000 2012-12-31 00:00:00.000
52378   21661   1579    2013-01-01 00:00:00.000 2013-12-31 00:00:00.000
57274   21660   1579    2014-01-01 00:00:00.000 3000-01-01 00:00:00.000

我尝试基于此使用递归 CTE: Possible recursive CTE query using date ranges 但我无法达到预期的效果。

我做了这个查询:

WITH splitDates(startDate,endDate, newDate,client, groupingId ) as
(
   SELECT m.datefrom as  startDate, m.dateTo
   , CASE 
   when year(m.dateFrom) <> year(m.dateto) then CAST(CAST(year(m.dateFrom) AS varchar) + '-' + CAST(12 AS varchar) + '-' + CAST(31 AS varchar) AS DATETIME)
    else m.dateTo
    end
   , m.legalEntityId, m.groupingId
   from adesse.dbo.membership m
   UNION ALL
   SELECT DATEADD(year, 1, startDate), 
   CAST(CAST(year(startDate)+1 AS varchar) + '-' + CAST(12 AS varchar) + '-'    + CAST(31 AS varchar) AS DATETIME)
   ,CAST(CAST(year(startDate)+1 AS varchar) + '-' + CAST(12 AS varchar) + '- ' + CAST(31 AS varchar) AS DATETIME)
   ,client, groupingId
   FROM splitDates
   WHERE year(startDate) <> year(endDate)
)
SELECT *
FROM  splitDates
where client  =  1579
order by startDate

但是结果不完整:(

startDate   endDate newDate client  groupingId
1997-01-01 00:00:00.000 1997-12-31 00:00:00.000 1997-12-31 00:00:00.000 1579    20008
1999-01-01 00:00:00.000 2004-12-31 00:00:00.000 1999-12-31 00:00:00.000 1579    20008
2000-01-01 00:00:00.000 2000-12-31 00:00:00.000 2000-12-31 00:00:00.000 1579    20008
2005-01-01 00:00:00.000 2008-12-31 00:00:00.000 2005-12-31 00:00:00.000 1579    20298
2006-01-01 00:00:00.000 2006-12-31 00:00:00.000 2006-12-31 00:00:00.000 1579    20298
2009-01-01 00:00:00.000 2009-12-31 00:00:00.000 2009-12-31 00:00:00.000 1579    21661
2010-01-01 00:00:00.000 2010-12-31 00:00:00.000 2010-12-31 00:00:00.000 1579    20399
2011-01-01 00:00:00.000 2013-12-31 00:00:00.000 2011-12-31 00:00:00.000 1579    21661
2012-01-01 00:00:00.000 2012-12-31 00:00:00.000 2012-12-31 00:00:00.000 1579    21661
2014-01-01 00:00:00.000 3000-01-01 00:00:00.000 2014-12-31 00:00:00.000 1579    21660
2015-01-01 00:00:00.000 2015-12-31 00:00:00.000 2015-12-31 00:00:00.000 1579    21660

感谢帮助

【问题讨论】:

    标签: sql-server tsql common-table-expression


    【解决方案1】:

    我不确定你的最后日期是否应该是 3000-01-01,但这应该可以

    CREATE TABLE members (membershipId INT, groupId INT, clientId INT, dateFrom DATETIME, dateTo DATETIME)
    INSERT INTO members VALUES 
    (2707,    20008,   1579,    '1997-01-01 00:00:00.000', '1997-12-31 00:00:00.000'),
    (20989,   20008,   1579,    '1999-01-01 00:00:00.000', '2004-12-31 00:00:00.000'),
    (39874,   20298,   1579,    '2005-01-01 00:00:00.000', '2008-12-31 00:00:00.000'),
    (50295,   21661,   1579,    '2009-01-01 00:00:00.000', '2009-12-31 00:00:00.000'),
    (50988,   20399,   1579,    '2010-01-01 00:00:00.000', '2010-12-31 00:00:00.000'),
    (52378,   21661,   1579,    '2011-01-01 00:00:00.000', '2013-12-31 00:00:00.000'),
    (57274,   21660,   1579,    '2014-01-01 00:00:00.000', '3000-01-01 00:00:00.000')
    
    ;
    
    WITH cte AS 
    (
        SELECT 
            membershipId,
            groupId,
            clientId,
            dateFrom,
            DATEADD(day, -1, DATEADD(YEAR,1,dateFrom)) newDateTo,
            dateTo 
        FROM 
            members
        UNION ALL
            SELECT 
                m.membershipId,
                m.groupId,
                m.clientId,
                DATEADD(YEAR,1,c.dateFrom),
                DATEADD(day, -1, DATEADD(YEAR,2,c.dateFrom)),
                c.dateto
            FROM 
                members m
                JOIN cte c ON  c.membershipId = m.membershipId
                               AND DATEADD(YEAR,1,c.dateFrom) < m.dateTo
    )
    SELECT  
        membershipId,
        groupId,
        clientId,
        dateFrom,
        newDateTo dateTo
    FROM 
        cte
    ORDER BY 
        membershipId, dateFrom
    OPTION (MAXRECURSION 0);
    
    DROP TABLE members
    

    SQL Fiddle

    【讨论】:

    • 太好了,效果很好。在日期中,我们有 3000 而不是 NULL 用于活动的,我只是添加一个条件来限制数据。非常感谢
    • 这是按原样使用开始日期。如果您希望开始日期是一年中的第一天,无论是 1 月 1 日还是 6 月 1 日,您可以在首先选择 datefrom 和 DATEADD(day, -1, DATEADD(YEAR,1,DATEADD(YEAR, DATEDIFF(YEAR, 0, dateFrom), 0))) newDateTo 作为 newdateto
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-21
    • 1970-01-01
    • 1970-01-01
    • 2015-03-16
    • 2020-02-28
    • 2013-11-09
    • 2013-01-04
    相关资源
    最近更新 更多