使用结果集在条目之间生成日期

时间:2017-07-14 16:07:47

标签: sql sql-server sql-server-2008

我非常基本的SQL用户。我知道基础知识和一些中级技能,但是我在编写这个查询时遇到了麻烦。

我有一个名为History的表,它包含特定数据的唯一条目。以下是该表格的简化版本:

+-------------------------+----+-----------+-------+
| Time                    | ID | ChangeNum | Value |
+-------------------------+----+-----------+-------+
| 2014-07-03 00:00:00.000 | 3  | 0         |  A
+-------------------------+----+-----------+-------+
| 2014-10-02 00:00:00.000 | 3  | 1         |  B
+-------------------------+----+-----------+-------+
| 2014-11-27 00:00:00.000 | 3  | 2         |  C
+-------------------------+----+-----------+-------+
| 2015-01-15 00:00:00.000 | 3  | 3         |  D
+-------------------------+----+-----------+-------+
| 2015-02-14 00:00:00.000 | 3  | 4         |  E
+-------------------------+----+-----------+-------+
| 2015-09-02 00:00:00.000 | 3  | 5         |  F
+-------------------------+----+-----------+-------+
| 2015-09-04 00:00:00.000 | 3  | 6         |  G
+-------------------------+----+-----------+-------+
| 2016-09-13 00:00:00.000 | 3  | 7         |  H
+-------------------------+----+-----------+-------+
| 2016-09-14 00:00:00.000 | 3  | 8         |  I 
+-------------------------+----+-----------+-------+
| 2017-02-12 00:00:00.000 | 3  | 9         |  J
+-------------------------+----+-----------+-------+
| 2017-02-18 00:00:00.000 | 3  | 10        |  K
+-------------------------+----+-----------+-------+

我需要做的是创建一个在这些日期范围之间生成数据的视图,同时保持其余值不变。例如,这是表格应该是什么样子的子集

    +-------------------------+----+-----------+
    | Time                    | ID | ChangeNum |
    +-------------------------+----+-----------+
    | 2014-07-03 00:00:00.000 | 3  | 0         |
    +-------------------------+----+-----------+
    | 2014-07-04 00:00:00.000 | 3  | 0         |
    +-------------------------+----+-----------+
    | 2014-07-05 00:00:00.000 | 3  | 0         |
    +-------------------------+----+-----------+
    | 2014-07-04 00:00:00.000 | 3  | 0         |
    +-------------------------+----+-----------+
    |        truncated for readability ...     |
    +-------------------------+----+-----------+
    | 2014-10-01 00:00:00.000 | 3  | 0         |
    +-------------------------+----+-----------+
    | 2014-10-02 00:00:00.000 | 3  | 1         |
    +-------------------------+----+-----------+

我见过这样的帖子,我可以使用CTE生成日期范围,这很简单。但是,这涉及循环历史表中的结果集,获取日期范围的下限和上限(第一行的时间字段,然后是下一行的时间字段),然后生成这些行之间的数据这可能比我看起来更容易,但我有点失落。我最初的想法是使用游标,但我不知道如何在表格中滞后/领先的情况下执行此操作。有帮助吗?感谢。

2 个答案:

答案 0 :(得分:1)

这是一种有趣的方式,虽然我根据您的示例数据做出一些假设:

size = len(l)
idxl = [random.choice([j for j in range(size) if j != i]) for i in range(size)]

for idx in idxl:
    # do something with the element at idx

修改更改了内容,以便它适用于所有ID,即使是使用不同的日期和ChangeNums:

--This is your current table
CREATE TABLE #TEST
(timefield datetime,
id int,
ChangeNum int)

INSERT INTO #TEST (TIMEFIELD, ID, CHANGENUM)
VALUES
('2014-07-03 00:00:00.000', 3, 0),
('2014-10-02 00:00:00.000', 3, 1),
('2014-11-27 00:00:00.000', 3, 2),
('2015-01-15 00:00:00.000', 3, 3),
('2015-02-14 00:00:00.000', 3, 4)

--This is your destination table
CREATE TABLE #TEST2
(timefield datetime,
 id int,
 ChangeNum int)

--This is where we INSERT from your source to destination table
DECLARE @TIMEFIELD datetime = '2014-07-03 00:00:00.000' --Your start date
DECLARE @ChangeNum int = 0 --Starting ChangeNum

WHILE @TIMEFIELD <= '2015-02-14 00:00:00.000' --Your end date
BEGIN
 INSERT INTO #TEST2
 SELECT @TIMEFIELD, 3, @ChangeNum;

 SET @TIMEFIELD = DATEADD(DD, 1, @TIMEFIELD);

IF EXISTS (SELECT * FROM #TEST WHERE TIMEFIELD = @TIMEFIELD)
  BEGIN
   SET @ChangeNum = (SELECT ChangeNum FROM #TEST WHERE TIMEFIELD = @TIMEFIELD)
   --This part can be modified to account for more columns
  END
END

SELECT * FROM #TEST2 --The new table

答案 1 :(得分:0)

这很有趣。正如肖恩所说,你需要一个计数表或者我的话需要一个日期表。 This is where I got the Dates table from.如果两次更改在同一天进行,则不会考虑将会发生什么。我还缩短了时间范围,使测试更容易。这确实会导致返回多个ID。

CREATE TABLE #test (ID INT, ChangeNum INT, [Value] varchar(1), [Time] datetime);

    DECLARE @StartDate datetime
        , @CutoffDate datetime;

INSERT INTO #test ([time], ID, ChangeNum, [Value])
VALUES 
('2011-07-03 00:00:00.000',  3, 0, 'A'),
('2011-07-10 00:00:00.000',  3, 1, 'B'),
('2011-07-15 00:00:00.000',  3, 2, 'C'),    
('2011-07-01 00:00:00.000',  2, 0, 'Q'),
('2011-07-06 00:00:00.000',  2, 1, 'R'),
('2011-08-03 00:00:00.000',  2, 2, 'S');    

    SELECT @StartDate = MIN([Time])
        ,@CutoffDate = MAX([time])
    FROM #test;

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
    )
     ,ChangeRanges
    AS (
        SELECT ID
            , [Time] BEGIN_DATE
            , CASE WHEN LEAD ([Time], 1,0) OVER (PARTITION BY ID ORDER BY ID, [time]) = '1900-01-01 00:00:00.000' THEN [Time] 
                    ELSE DATEADD(DAY, -1, LEAD ([Time], 1,0) OVER (PARTITION BY ID ORDER BY ID, [time])) END END_DATE 
            , ChangeNum
        FROM #test
)       
    SELECT d.d [DATE]
            ,cr.ID
            ,cr.ChangeNum
    FROM ChangeRanges cr
    JOIN Dates d ON d.d >= cr.BEGIN_DATE AND d.d <= cr.END_DATE
    WHERE ID = 3
    GROUP BY cr.ID, d.d, cr.ChangeNum
    ORDER BY cr.ID, d.d

DROP TABLE #test