在T-SQL中从频率列创建修改的行

时间:2017-06-01 15:21:36

标签: sql sql-server

我需要一些关于在SQL上的频率列上为每个计数创建行的有效方法的想法。 (SQL Server 2016)

数据:
我有一张桌子上有人们生病的日期以及他们说他们将缺席多少天:

BEGIN_DATE               DAYS_SICK
2011-01-01 00:00:00.000  3
2011-01-01 00:00:00.000  3
2011-01-01 00:00:00.000  1
2011-01-02 00:00:00.000  2
2011-01-02 00:00:00.000  3
2011-01-04 00:00:00.000  4
2011-01-04 00:00:00.000  4
2011-01-04 00:00:00.000  3

我想将其翻译成一个表格,其中每一行代表一年中的一天,我会计算那天生病的人数。

DATE                     PEOPLE_SICK
2011-01-01 00:00:00.000  3 
2011-01-02 00:00:00.000  4
2011-01-03 00:00:00.000  4
2011-01-04 00:00:00.000  4
2011-01-05 00:00:00.000  3
2011-01-06 00:00:00.000  3
2011-01-07 00:00:00.000  2

例如:

  • 2011-01-01有3人生病,2人生病3天,只有当天生病。输出为3。
  • 现在2011-01-02又有2名(不同)人员生病,但前一天有2人表示他们也会错过那一天,所以产量为4。
  • 2011-01-03没有人生病,但2天前有2人表示他们会错过当天加上前一天的2人。输出为4。
  • 等等...

我目前正在通过迭代输入中的每一行然后循环频率,根据需要在新表上添加或更新行来执行此操作,但这需要花费大量时间。

还有其他方法可以更有效地做到这一点吗?

1 个答案:

答案 0 :(得分:1)

这根本不会与周末打交道,但可以帮助你入门。此外,如果有一个经常运行的查询,我会构建一个DATE DIM表并使用它而不是Dates CTE。 Where I got the DATE DIM code from.

CREATE TABLE #test (ID int IDENTITY(1,1), BEGIN_DATE datetime, DAYS_SICK int);

DECLARE @StartDate datetime = '2011-01-01'
, @CutoffDate datetime = '2011-01-10';

INSERT INTO #test (BEGIN_DATE, DAYS_SICK)
VALUES 
('2011-01-01 00:00:00.000',  3),
('2011-01-01 00:00:00.000',  3),
('2011-01-01 00:00:00.000',  1),
('2011-01-02 00:00:00.000',  2),
('2011-01-02 00:00:00.000',  3),
('2011-01-04 00:00:00.000',  4),
('2011-01-04 00:00:00.000',  4),
('2011-01-04 00:00:00.000',  3);

WITH Dates
AS (SELECT d
    FROM (
        SELECT d = DATEADD(DAY, rn - 1, @StartDate)
        FROM (SELECT TOP (DATEDIFF(DAY, @StartDate, @CutoffDate)) rn = ROW_NUMBER() OVER (
                    ORDER BY s1.[object_id])
            FROM sys.all_objects AS s1
            CROSS JOIN sys.all_objects AS s2
            ORDER BY s1.[object_id]
            ) AS x
        ) AS y
    )
    ,SickRanges
AS (
    SELECT BEGIN_DATE
        ,DATEADD(DAY, DAYS_SICK - 1, BEGIN_DATE) END_DATE
    FROM #test
    )
SELECT d.d [DATE]
    ,count(1) PEOPLE_SICK
FROM SickRanges sr
JOIN Dates d ON d.d BETWEEN sr.BEGIN_DATE AND sr.END_DATE
GROUP BY d.d
ORDER BY d.d

DROP TABLE #test