Sql 使用结果集在条目之间生成日期
我是一个非常基本的SQL用户。我知道基础知识和一些中级技能,但是我在编写这个查询时遇到了麻烦 我有一个名为History的表,其中包含特定数据段的唯一条目。下面是此表的精简版本:Sql 使用结果集在条目之间生成日期,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,我是一个非常基本的SQL用户。我知道基础知识和一些中级技能,但是我在编写这个查询时遇到了麻烦 我有一个名为History的表,其中包含特定数据段的唯一条目。下面是此表的精简版本: +-------------------------+----+-----------+-------+ | Time | ID | ChangeNum | Value | +-------------------------+----+-----------+-------+ |
+-------------------------+----+-----------+-------+
| 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生成一个日期范围,这很简单。但是,这涉及到在历史记录表中循环一个结果集,获取日期范围的上下限(第一行的时间字段,然后是下一行的时间字段),然后在这些行之间生成数据。这可能比我想象的要容易,但我有点迷路了。我最初的想法是使用游标,但我不知道在表中落后/领先的情况下如何做到这一点。有什么帮助吗?谢谢。下面是一个有趣的方法,尽管我根据您的示例数据做了一些假设:
--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
编辑更改的内容,使其适用于所有ID,即使是不同的日期和更改:
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),
('2014-11-27 00:00:00.000', 2, 2),
('2015-01-15 00:00:00.000', 2, 3),
('2015-02-14 00:00:00.000', 2, 4),
('2014-10-02 00:00:00.000', 1, 1),
('2014-11-27 00:00:00.000', 1, 2),
('2015-01-15 00:00:00.000', 1, 3),
('2015-02-14 00:00:00.000', 1, 4)
CREATE TABLE #TEST2
(timefield datetime,
id int,
ChangeNum int)
DECLARE @ID int = (SELECT MIN(ID) FROM #TEST)
DECLARE @ChangeNum int = (SELECT MIN(ChangeNum) FROM #TEST WHERE @ID = ID)
DECLARE @TIMEFIELD datetime = (SELECT MIN(TIMEFIELD) FROM #TEST WHERE @ID = ID)
WHILE @ID <= (SELECT MAX(ID) FROM #TEST)
BEGIN
WHILE @TIMEFIELD <= (SELECT MAX(TIMEFIELD) FROM #TEST WHERE ID = @ID)
BEGIN
INSERT INTO #TEST2
SELECT @TIMEFIELD, @ID, @ChangeNum
SET @TIMEFIELD = DATEADD(DD, 1, @TIMEFIELD)
IF EXISTS (SELECT * FROM #TEST WHERE TIMEFIELD = @TIMEFIELD AND ID = @ID)
BEGIN
SET @ChangeNum = (SELECT ChangeNum FROM #TEST WHERE TIMEFIELD = @TIMEFIELD AND ID = @ID);
END
END
IF EXISTS (SELECT MIN(ID) FROM #TEST WHERE ID > @ID)
BEGIN
SET @ID = (SELECT MIN(ID) FROM #TEST WHERE ID > @ID)
SET @ChangeNum = (SELECT MIN(ChangeNum) FROM #TEST WHERE @ID = ID)
SET @TIMEFIELD = (SELECT MIN(TIMEFIELD) FROM #TEST WHERE @ID = ID)
END
END
SELECT * FROM #TEST2
这很有趣。正如肖恩所说,你需要一个理货表,或者用我的话来说是一个日期表。这并没有考虑到如果在同一天进行两次更改会发生什么。我还缩短了时间框架,使测试更容易。这并不说明返回了多个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
您肯定不需要光标来执行此操作。你需要一个理货台。这两个答案都使用理货台,我从来没有听说过。这可以通过使用常规SQL语法(例如不使用变量或表创建)来实现吗?此外,这不是一个很好的“缺口和孤岛”解决方案的候选方案,还是我弄错了?@psrpsrpsr我的答案不使用理货表,但我认为没有变量或理货表就无法做到这一点。你可能需要一个或另一个来生成丢失的日期记录。只是澄清我的答案并不需要理货表,这不是绝对必要的。我同意@AaronDietz。理货台不是绝对必要的。有太多的情况下,循环解决方案因为太麻烦或无法工作,例如报告。如果你只需要SSMS中的列表,循环就可以了。我也没有注意到你在2008年。我的答案在那个版本中不起作用。我要说的是,在顶部标记您的答案SQL Server 2012+选项,如果其他人碰巧路过,这仍然是一个简洁而有用的答案。太棒了-这相当直接。我根据我的环境的具体情况修改了它——需要某些表和变量,我特定的SQL引擎语法,等等——但这很有魅力。谢谢@dxh9845很高兴听到这个消息,这是一个有趣的事情!我不确定你现在是否需要这个,但我不喜欢它是为ID=3硬编码的,而且几乎不可伸缩,所以我编辑了一个新的答案,它将适用于每个ID/ChangeNum/Date组合。老实说,非常有趣-我让它在循环中工作,但你比我快。谢谢