📜  将日期范围转换为每日记录的 SQL 查询

📅  最后修改于: 2022-05-13 01:55:02.013000             🧑  作者: Mango

将日期范围转换为每日记录的 SQL 查询

在本文中,我们将了解如何将日期范围转换为每日记录,但在此之前,我们需要清楚地了解它对我们有什么帮助。

假设,我们有一个数据库,我们想在其中存储特定班级学生的所有项目详细信息记录。在那里我们可以看到学生完成他/她的项目需要多长时间,但它以日期范围的形式保存。例如,学生 1 从 2021 年 11 月 20 日开始做这个项目,并在 2021 年 11 月 24 日完成。那么,她完成了多长时间? 5天,简单。但是,当我们处理数百万个数据时,计算似乎并不那么简单。这就是为什么我们要将其从日期范围转换为每日记录的原因。因为我们可以很容易地在单独的行上应用聚合函数,这将使我们的分析更简单。

在这里,我们将使用“计数表”“数字表”。

数字表/Tally 表:数字表被称为 SQL Server 的“瑞士军刀”。它可以用更快的基于集合的操作来代替循环,扩展数据集,插入测试数据,以及更多其他事情。它将只有一列,其中包含从 0 或 1 到您可能需要的最高数字的连续数字。

句法:

--create a table CREATE TABLE Sample(SampleId  int  not null) 
DECLARE @NoOfRows int SET @NoOfRows = 1000000  
--We can add as many rows as you want  ;
WITH  P0 AS (SELECT 1 AS C UNION ALL SELECT 1),
--2 rows  P1 AS (SELECT 1 AS C FROM Pass0 AS A, P0 AS B),
--4 rows  P2 AS (SELECT 1 AS C FROM Pass1 AS A, P1 AS B),
--16 rows  P3 AS (SELECT 1 AS C FROM Pass2 AS A, P2 AS B),
--256 rows  P4 AS (SELECT 1 AS C FROM Pass3 AS A, P3 AS B),
--65536 rows  P5 AS (SELECT 1 AS C FROM Pass4 AS A, P4 AS B),
--4,294,967,296 rows Tally AS (SELECT row_number()
 over(ORDER BY C) AS Number FROM P5) 
INSERT Sample (SampleId) SELECT Number 
 FROM Tally  WHERE Number <= @NoOfRows   

注意:以下语法由 T-SQL 大师Itzik Ben-Gan发明

由于我们使用的是WITH子句,因此可以将其用于INSERTSELECT 。在最后一行, WHERE Number <= @NoOfRows 可以轻松调整,我们可以放置任何范围。

现在,让我们看看如何 SQL 查询将日期范围转换为每天的记录。

第 1 步:创建数据库并使用它

使用以下命令创建数据库并使用它。

询问:

CREATE DATABASE GFG_Demo; USE GFG_Demo;  

输出:

第 2 步:表定义

我们在数据库中有以下工作完成表。

询问:

CREATE TABLE WorkDone (ID VARCHAR(10) 
NOT NULL , StartDate DATE NOT NULL , 
EndDate DATE NOT NULL);   

输出:

第 3 步:插入值

以下命令用于将值插入表中。

询问:

INSERT INTO WorkDone(ID,StartDate,EndDate)
VALUES('S1', '2021-01-02','2021-01-06'),
('S2', '2021-02-03','2021-02-07'),
('S3', '2021-03-02','2021-03-09'),   
('S4', '2021-01-05','2021-01-11'),
('S5', '2021-02-22','2021-02-26'),
('S6', '2021-03-12','2021-03-17'), 
('S7', '2021-01-05','2021-01-13'),
('S8', '2021-02-05','2021-02-09'),
('S9', '2021-03-13','2021-03-18'), 
('S10', '2021-01-07','2021-01-12'),
('S11', '2021-02-06','2021-02-10'),
('S12', '2021-03-12','2021-03-19'), 
('S13', '2021-01-12','2021-01-15'),
('S14', '2021-02-20','2021-02-24'),
('S15', '2021-03-10','2021-03-15'),
('S16', '2021-01-21','2021-01-28'),
('S17', '2021-02-21','2021-02-26'),
('S18', '2021-03-11','2021-03-16'), 
('S19', '2021-01-23','2021-01-28'),
('S20', '2021-02-12','2021-02-16'),
('S21', '2021-03-08','2021-03-10'),
('S20', '2021-01-08','2021-01-14'),
('S21', '2021-02-05','2021-02-09'),
('S22', '2021-03-06','2021-03-09'), 
('S23', '2021-01-14','2021-01-16'),
('S24', '2021-02-22','2021-02-26'),
('S25', '2021-03-05','2021-03-13');   

输出:

第五步:查看表的数据

询问:

SELECT * FROM WorkDone; 

输出:

第 6 步:扩大日期范围

询问:

WITH S00(N) AS (SELECT 1 UNION ALL SELECT 1), 
S02(N) AS (SELECT 1 FROM S00 a, S00 b) , 
S04(N) AS (SELECT 1 FROM S02 a, S02 b) ,
S08(N) AS (SELECT 1 FROM S04 a, S04 b) , 
S16(N) AS (SELECT 1 FROM S08 a, S08 b) ,
S32(N) AS (SELECT 1 FROM S16 a, S16 b) ,
CteTally(N) AS (SELECT ROW_NUMBER() 
OVER (ORDER BY (SELECT NULL)) FROM S32) ,
DateRange AS (SELECT SeparatedDate = 
DATEADD(DAY,N - 1,'2021-01-01') 
FROM CteTally    WHERE N <= 365 ) 
SELECT * FROM WorkDone w JOIN DateRange 
d ON d.SeparatedDate >= w.[StartDate] 
AND d.SeparatedDate <= w.[EndDate];   

在这里,在第一个 CTE S00 中,两行连接在一起,这就是为什么它给我们 2 行。第二个 CTE S02 与自身交叉连接,结果为 4 行。其余行也继续执行相同的过程,产生 16、256、65536、…. 行,最后一个将产生 2^32 行。

注意: 2^32 是 SQL Server 中整数可以容纳的最大数字。

  • 窗口函数ROW_NUMBER用于为每一行分配一个数字。
  • 现在,我们在OVER子句中的ORDER BY中使用的子查询呢?它用于欺骗服务器不对数据集进行排序。对这么多行进行排序可能会严重降低查询的性能。该查询用于为每一行分配一个唯一的序号,从数字 1 开始。
  • 请记住,它用于生成数字,而不是日期。这就是我们使用DATEADD函数将数字转换为日期的原因。这将生成 2021 年的所有日期。
  • 为了探索我们的样本数据,我们必须使用范围连接将计数表连接到工作完成表。

输出:

在这里,我们看到它已成功扩展。但是,它们的顺序不正确,我们可以通过在查询的最后添加 ORDER BY函数轻松地将它们按正确的顺序排列。正如我们之前提到的,计数表很宽泛。我们可以设置我们想要的限制,它不会对性能产生太大影响。