如何按日期范围将单个记录拆分/外推到多个记录中

时间:2015-09-22 04:07:57

标签: sql sql-server

我的数据如下,包含开始日期和结束日期。记录可以在同一天开始和结束,也可以持续很多天。

我想扩展我每天有一个条目的每条记录。例如:

AAAA 09/10/2015 15:30 09/11/2015 16:00

会变成

AAAA 09/10/2015 15:30 09/11/2015 00:00

AAAA 09/11/2015 00:00 09/11/2015 16:00

这可以在查询中使用for each循环吗?我不知道该怎么办。

非常感谢任何帮助。

提前致谢。

3 个答案:

答案 0 :(得分:2)

这是拥有日历查找表的很多情况之一。有许多日历表脚本的例子可以根据您的需要构建简单或健壮的东西,但是让我们假装您拥有的是日历表中的日期列表。

通常你只是使用BETWEEN加入,但由于你想要保留开始和结束时间部分,你需要一些额外的逻辑,使用CASE表达式,{{1 }}和CAST()你可以得到你想要的东西,像这样:

DATEADD()

答案 1 :(得分:1)

您可以使用Itzik Ben-Gan cascaded/stack CTEs动态生成日历表:

DECLARE @Range AS INT

SELECT TOP 1 @Range = 
    DATEDIFF(DAY, MIN(start_date), MAX(end_date)) + 1
FROM yourTable
GROUP BY col1 
ORDER BY DATEDIFF(DAY, MIN(start_date), MAX(end_date)) + 1 DESC


;WITH E1(N) AS( -- 10 ^ 1 = 10 rows
    SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
Tally(N) AS(
    SELECT TOP(@Range) ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
    FROM E4
),
CteMinMax(col1, sdt, edt) AS(
    SELECT col1, MIN(start_date), MAX(end_date)
    FROM yourTable  
    GROUP BY col1
),
CteDates(col1, sdt, edt) AS(
    SELECT
        m.col1,
        DATEADD(DAY, t.N-1, CAST(m.sdt AS DATE)),
        DATEADD(DAY, t.N, CAST(m.sdt AS DATE))
    FROM CteMinMax m
    CROSS JOIN Tally t
    WHERE DATEADD(DAY, t.N-1, CAST(m.sdt AS DATE)) < DATEADD(DAY, 1, CAST(m.edt AS DATE))
)
SELECT
    t.col1,
    start_date =
        CASE
            WHEN t.start_date > d.sdt THEN t.start_date
            ELSE CAST(d.sdt AS DATETIME)
        END,
    end_date = 
        CASE
            WHEN t.end_date >= d.edt THEN CAST(d.edt AS DATETIME)
            ELSE t.end_date
        END
FROM yourTable t
INNER JOIN CteDates d
    ON d.sdt >= CAST(t.start_date AS DATE) 
    AND d.sdt < DATEADD(DAY, 1, CAST(t.end_date AS DATE))

SQL Fiddle

答案 2 :(得分:0)

谢谢哈特。你的逻辑稍有改变。

SELECT col1
      ,CASE WHEN CAST(start_dt AS DATE) = b.cal_dt THEN start_dt
            WHEN CAST(end_dt AS DATE) = b.cal_dt THEN 
                CAST(CAST(end_dt AS DATE)AS DATETIME)
                ELSE CAST(cal_dt AS DATETIME)
           END AS start_dt
           ,CASE  WHEN CAST(start_dt AS DATE) = CAST(end_dt AS DATE) THEN end_dt
            WHEN CAST(start_dt AS DATE) = b.cal_dt THEN
                    CAST(DATEADD(day,1,CAST(start_dt AS DATE))AS DATETIME)
                    WHEN CAST(end_dt AS DATE) = b.cal_dt THEN end_dt
                    ELSE CAST(DATEADD(day,1,cal_dt) AS DATETIME)
                END AS end_dt
        FROM Table1 a
        JOIN lkp_Calendar b
          ON b.cal_dt BETWEEN CAST(start_dt AS DATE) AND CAST(end_Dt AS DATE)