为范围内的每个日期创建一行,并为 SQL 中的记录在日期范围内的每一天加 1
Create a row for each date in a range, and add 1 for each day within a date range for a record in SQL
假设我有一个日期范围,@StartDate = 2022-01-01 和@EndDate = 2022-02-01,这是一个报告期。
此外,我还有客户记录,其中每个客户都有一个 LIVE Date 和一个 ServiceEndDate(或者 ServiceEndDate = NULL,因为他们是持续的客户)
一些客户的生效日期和服务结束日期范围可能会超出报告期范围。我只想报告他们在此期间是客户的天数。
Name
LiveDate
ServiceEndDate
Tom
2021-10-11
2022-01-13
Mark
2022-11-13
2022-02-15
Andy
2022-01-02
2022-02-10
Rob
2022-01-09
2022-01-14
我想创建一个 table,其中 A 列是日期(在报告期间的每个日期之间迭代),B 列是该日期作为客户的客户数量的总和.
像这样
Date
NumberOfCustomers
2022-01-01
2
2022-01-02
3
2022-01-03
3
2022-01-04
3
2022-01-05
3
2022-01-06
3
2022-01-07
3
2022-01-08
3
2022-01-09
4
2022-01-10
4
2022-01-11
4
2022-01-12
4
2022-01-13
4
2022-01-14
3
2022-01-15
3
以此类推直到@EndDate
结束
非常感谢任何帮助,谢谢。
您可以将您的 table 加入包含您需要的所有日期的日历 table:
with calendar as
(select cast('2022-01-01' as datetime) as d
union all select dateadd(day, 1, d)
from calendar
where d < '2022-02-01')
select d as "Date", count(*) as NumberOfCustomers
from calendar inner join table_name
on d between LiveDate and coalesce(ServiceEndDate, '9999-12-31')
group by d;
我个人建议使用 Tally 而不是 rCTE,因为 Tally 显着 更高效。
SELECT *
INTO dbo.YourTable
FROM (VALUES('Tom ',CONVERT(date,'2021-10-11 '),CONVERT(date,'2022-01-13')),
('Mark',CONVERT(date,' 2022-11-13'),CONVERT(date,' 2022-02-15')),
('Andy',CONVERT(date,' 2022-01-02'),CONVERT(date,' 2022-02-10')),
('Rob ',CONVERT(date,'2022-01-09 '),CONVERT(date,'2022-01-14')))V(Name,LiveDate,ServiceEndDate);
GO
SELECT *
FROM dbo.YourTable;
GO
DECLARE @StartDate date = '20220101',
@EndDate date = '20220201';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT 0 AS I
UNION ALL
SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3), --up to 1,000 days
Dates AS(
SELECT DATEADD(DAY, T.I, @StartDate) AS Date
FROM Tally T)
SELECT D.Date,
COUNT(YT.[Name]) AS NumberOfCustomers
FROM Dates D
LEFT JOIN dbo.YourTable YT ON D.[Date] >= YT.LiveDate
AND (D.[Date] <= YT.ServiceEndDate
OR YT.ServiceEndDate IS NULL)
GROUP BY D.[Date]
ORDER BY D.[Date];
GO
DROP TABLE dbo.YourTable;
请注意,结果并未反映您的预期结果,我怀疑您的预期结果是错误的。例如,您有 2 个人生活在 2022-01-01,但是,只有 1 个人在该日期生活:Tom。
此解决方案也永远不会将标记为“实时”(另一个答案中的 rCTE 方法也不会),因为它们的结束日期是 在 它们的实时日期之前。如果有人可以在开始之前结束服务,我建议你有数据质量问题,你应该在 table 中添加 CHECK
CONSTRAINT
以确保 ServiceEndDate
是 >= LiveDate
.
假设我有一个日期范围,@StartDate = 2022-01-01 和@EndDate = 2022-02-01,这是一个报告期。
此外,我还有客户记录,其中每个客户都有一个 LIVE Date 和一个 ServiceEndDate(或者 ServiceEndDate = NULL,因为他们是持续的客户)
一些客户的生效日期和服务结束日期范围可能会超出报告期范围。我只想报告他们在此期间是客户的天数。
Name | LiveDate | ServiceEndDate |
---|---|---|
Tom | 2021-10-11 | 2022-01-13 |
Mark | 2022-11-13 | 2022-02-15 |
Andy | 2022-01-02 | 2022-02-10 |
Rob | 2022-01-09 | 2022-01-14 |
我想创建一个 table,其中 A 列是日期(在报告期间的每个日期之间迭代),B 列是该日期作为客户的客户数量的总和.
像这样
Date | NumberOfCustomers |
---|---|
2022-01-01 | 2 |
2022-01-02 | 3 |
2022-01-03 | 3 |
2022-01-04 | 3 |
2022-01-05 | 3 |
2022-01-06 | 3 |
2022-01-07 | 3 |
2022-01-08 | 3 |
2022-01-09 | 4 |
2022-01-10 | 4 |
2022-01-11 | 4 |
2022-01-12 | 4 |
2022-01-13 | 4 |
2022-01-14 | 3 |
2022-01-15 | 3 |
以此类推直到@EndDate
结束非常感谢任何帮助,谢谢。
您可以将您的 table 加入包含您需要的所有日期的日历 table:
with calendar as
(select cast('2022-01-01' as datetime) as d
union all select dateadd(day, 1, d)
from calendar
where d < '2022-02-01')
select d as "Date", count(*) as NumberOfCustomers
from calendar inner join table_name
on d between LiveDate and coalesce(ServiceEndDate, '9999-12-31')
group by d;
我个人建议使用 Tally 而不是 rCTE,因为 Tally 显着 更高效。
SELECT *
INTO dbo.YourTable
FROM (VALUES('Tom ',CONVERT(date,'2021-10-11 '),CONVERT(date,'2022-01-13')),
('Mark',CONVERT(date,' 2022-11-13'),CONVERT(date,' 2022-02-15')),
('Andy',CONVERT(date,' 2022-01-02'),CONVERT(date,' 2022-02-10')),
('Rob ',CONVERT(date,'2022-01-09 '),CONVERT(date,'2022-01-14')))V(Name,LiveDate,ServiceEndDate);
GO
SELECT *
FROM dbo.YourTable;
GO
DECLARE @StartDate date = '20220101',
@EndDate date = '20220201';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT 0 AS I
UNION ALL
SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3), --up to 1,000 days
Dates AS(
SELECT DATEADD(DAY, T.I, @StartDate) AS Date
FROM Tally T)
SELECT D.Date,
COUNT(YT.[Name]) AS NumberOfCustomers
FROM Dates D
LEFT JOIN dbo.YourTable YT ON D.[Date] >= YT.LiveDate
AND (D.[Date] <= YT.ServiceEndDate
OR YT.ServiceEndDate IS NULL)
GROUP BY D.[Date]
ORDER BY D.[Date];
GO
DROP TABLE dbo.YourTable;
请注意,结果并未反映您的预期结果,我怀疑您的预期结果是错误的。例如,您有 2 个人生活在 2022-01-01,但是,只有 1 个人在该日期生活:Tom。
此解决方案也永远不会将标记为“实时”(另一个答案中的 rCTE 方法也不会),因为它们的结束日期是 在 它们的实时日期之前。如果有人可以在开始之前结束服务,我建议你有数据质量问题,你应该在 table 中添加 CHECK
CONSTRAINT
以确保 ServiceEndDate
是 >= LiveDate
.