使用 Tally Table 填写 SQL 中按日期排序的缺失日期
Fill Missing Dates In a Date-Sequenced in SQL using Tally Table
我在数据库中有一个 table 包含租户数量的数据库,每个租户都列出了他们每个日期的销售额记录。在某些情况下,租户没有销售,特别是 date/s,因此没有销售的日期在 table 中没有记录,打破了正确的日期顺序。请参阅下面的示例 table:
我在 SQL 中使用了这个 select 查询来显示上面的输出
select tenant, date, sales
from tblSales
where date between '01/01/2015' and '01/05/2014'
我需要的正确输出:根据 where 子句上的 selected 日期范围显示完整日期,当租户在特定日期没有记录时,查询应添加日期记录在该特定租户中,只需在销售列中添加空值,如下图所示:
- 作为我最初的解决方案,我想创建一个临时 table 插入一个基于日期范围 selected 的日期序列,并使用它与实际 table 左连接].
这是我开始的:
@dateFrom datetime = '02/01/2015',
@dateTo date = '02/05/2015'
declare @MaxNumDays int
declare @Counter int
set @Counter = 0
set @MaxNumDays = DATEDIFF(day, @dateFrom , @dateto) + 1
create table #DSRTdate (
Date datetime
)
WHILE @Counter < @MaxNumDays
BEGIN
insert into #DSRTdate (Date) values (DATEADD(day,@Counter,@dateFrom ))
SET @Counter += 1
END
我使用上面的代码获取并插入临时 table 来自使用 selection 的序列数据,在上面的例子中,它插入 02/01/2015, 02/ 02/2015、02/03/2015、02/04/2015 和 02/05/2015
select tenantcode, date, sales
into #DSRT2
from DAILYMOD
where (date between @dateFrom and @dateTo)
select *
from #dsrtdate a
left join #DSRT2 b on a.date = b.date
order by b.tenantcode, a.date
然后我使用 left join 来显示缺少的日期,但这只会对一个租户产生影响,并且它也会使租户名称为空。像这样:
如有任何建议,我们将不胜感激。
您可以使用 Tally Table。
基本上,您使用 Tally Table
生成从 @startDate
到 @endDate
的日期序列,然后使用 CROSS JOIN
到 DISTINCT Item
生成所有 Date
-Item
组合。然后,结果将 LEFT-JOIN
编辑为 tblSales
以获得所需的输出。
DECLARE
@startDate DATE = '20140101',
@endDate DATE = '20140105';
WITH E1(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,E2(N) AS(SELECT 1 FROM E1 a, E1 b)
,E4(N) AS(SELECT 1 FROM E2 a, E2 b)
,Tally(N) AS(
SELECT TOP (DATEDIFF(DAY, @startDate, @endDate) + 1)
ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
FROM E4
)
,CteAllDates(Item, dt) AS(
SELECT x.Item, DATEADD(DAY, N - 1, @startDate)
FROM Tally
CROSS JOIN(
SELECT DISTINCT Item
FROM tblSales
WHERE [Date] BETWEEN @startDate AND @endDate
) AS x
)
SELECT d.*, ts.Sales
FROM CteAllDates d
LEFT JOIN tblSales ts
ON ts.Item = d.Item
AND ts.Date = d.dt
WHERE
ts.[Date] BETWEEN @startDate AND @endDate
ORDER BY d.Item, d.dt
这是一个替代方案。而不是级联 CTE
s,使用 sys.columns
生成 Tally Table
.:
DECLARE
@startDate DATE = '20140101',
@endDate DATE = '20140105';
WITH Tally(N) AS(
SELECT TOP (DATEDIFF(DAY, @startDate, @endDate) + 1)
ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
FROM sys.columns a, sys.columns b
)
,CteAllDates(Item, dt) AS(
SELECT x.Item, DATEADD(DAY, N - 1, @startDate)
FROM Tally
CROSS JOIN(
SELECT DISTINCT Item
FROM tblSales
WHERE [Date] BETWEEN @startDate AND @endDate
) AS x
)
SELECT d.*, ts.Sales
FROM CteAllDates d
LEFT JOIN tblSales ts
ON ts.Item = d.Item
AND ts.Date = d.dt
WHERE
ts.[Date] BETWEEN @startDate AND @endDate
ORDER BY d.Item, d.dt
结果
| Item | dt | Sales |
|---------|------------|--------|
| tenant1 | 2014-01-01 | 100 |
| tenant1 | 2014-01-02 | 100 |
| tenant1 | 2014-01-03 | 100 |
| tenant1 | 2014-01-04 | NULL |
| tenant1 | 2014-01-05 | 100 |
| tenant2 | 2014-01-01 | 100 |
| tenant2 | 2014-01-02 | NULL |
| tenant2 | 2014-01-03 | NULL |
| tenant2 | 2014-01-04 | 100 |
| tenant2 | 2014-01-05 | NULL |
| tenant3 | 2014-01-01 | 100 |
| tenant3 | 2014-01-02 | NULL |
| tenant3 | 2014-01-03 | 100 |
| tenant3 | 2014-01-04 | NULL |
| tenant3 | 2014-01-05 | 100 |
我lovewewesthemenace回答!投票并保存以供将来参考。这是另一个建议。在 Crystal 的 SQL 命令中使用此代码。左外连接到您的 table 销售额。它的长。这很乏味。但是,如果您不是 SQL 专家,它更容易理解,这很好地描述了我 :) 如果您找到了他的答案并且有效,那么请忽略此解决方案。
注意:除了满足在这样的 SQL 语句中使用 from 子句的要求外,from 子句没有任何作用。如果 tblSales 很大 table 考虑使用您的数据库中的一个,它的数据较少并且仍然保留到 tblSales 的外部连接。
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','01') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','02') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','03') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','04') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','05') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','06') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','07') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','08') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','09') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','10') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','11') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','12') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','13') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','14') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','15') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','16') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','17') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','18') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','19') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','20') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','21') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','22') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','23') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','24') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','25') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','26') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','27') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','28') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','29') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','30') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','31') as DATE) as salesdate
from tblSales
我在数据库中有一个 table 包含租户数量的数据库,每个租户都列出了他们每个日期的销售额记录。在某些情况下,租户没有销售,特别是 date/s,因此没有销售的日期在 table 中没有记录,打破了正确的日期顺序。请参阅下面的示例 table:
我在 SQL 中使用了这个 select 查询来显示上面的输出
select tenant, date, sales
from tblSales
where date between '01/01/2015' and '01/05/2014'
我需要的正确输出:根据 where 子句上的 selected 日期范围显示完整日期,当租户在特定日期没有记录时,查询应添加日期记录在该特定租户中,只需在销售列中添加空值,如下图所示:
- 作为我最初的解决方案,我想创建一个临时 table 插入一个基于日期范围 selected 的日期序列,并使用它与实际 table 左连接].
这是我开始的:
@dateFrom datetime = '02/01/2015',
@dateTo date = '02/05/2015'
declare @MaxNumDays int
declare @Counter int
set @Counter = 0
set @MaxNumDays = DATEDIFF(day, @dateFrom , @dateto) + 1
create table #DSRTdate (
Date datetime
)
WHILE @Counter < @MaxNumDays
BEGIN
insert into #DSRTdate (Date) values (DATEADD(day,@Counter,@dateFrom ))
SET @Counter += 1
END
我使用上面的代码获取并插入临时 table 来自使用 selection 的序列数据,在上面的例子中,它插入 02/01/2015, 02/ 02/2015、02/03/2015、02/04/2015 和 02/05/2015
select tenantcode, date, sales
into #DSRT2
from DAILYMOD
where (date between @dateFrom and @dateTo)
select *
from #dsrtdate a
left join #DSRT2 b on a.date = b.date
order by b.tenantcode, a.date
然后我使用 left join 来显示缺少的日期,但这只会对一个租户产生影响,并且它也会使租户名称为空。像这样:
如有任何建议,我们将不胜感激。
您可以使用 Tally Table。
基本上,您使用 Tally Table
生成从 @startDate
到 @endDate
的日期序列,然后使用 CROSS JOIN
到 DISTINCT Item
生成所有 Date
-Item
组合。然后,结果将 LEFT-JOIN
编辑为 tblSales
以获得所需的输出。
DECLARE
@startDate DATE = '20140101',
@endDate DATE = '20140105';
WITH E1(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,E2(N) AS(SELECT 1 FROM E1 a, E1 b)
,E4(N) AS(SELECT 1 FROM E2 a, E2 b)
,Tally(N) AS(
SELECT TOP (DATEDIFF(DAY, @startDate, @endDate) + 1)
ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
FROM E4
)
,CteAllDates(Item, dt) AS(
SELECT x.Item, DATEADD(DAY, N - 1, @startDate)
FROM Tally
CROSS JOIN(
SELECT DISTINCT Item
FROM tblSales
WHERE [Date] BETWEEN @startDate AND @endDate
) AS x
)
SELECT d.*, ts.Sales
FROM CteAllDates d
LEFT JOIN tblSales ts
ON ts.Item = d.Item
AND ts.Date = d.dt
WHERE
ts.[Date] BETWEEN @startDate AND @endDate
ORDER BY d.Item, d.dt
这是一个替代方案。而不是级联 CTE
s,使用 sys.columns
生成 Tally Table
.:
DECLARE
@startDate DATE = '20140101',
@endDate DATE = '20140105';
WITH Tally(N) AS(
SELECT TOP (DATEDIFF(DAY, @startDate, @endDate) + 1)
ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
FROM sys.columns a, sys.columns b
)
,CteAllDates(Item, dt) AS(
SELECT x.Item, DATEADD(DAY, N - 1, @startDate)
FROM Tally
CROSS JOIN(
SELECT DISTINCT Item
FROM tblSales
WHERE [Date] BETWEEN @startDate AND @endDate
) AS x
)
SELECT d.*, ts.Sales
FROM CteAllDates d
LEFT JOIN tblSales ts
ON ts.Item = d.Item
AND ts.Date = d.dt
WHERE
ts.[Date] BETWEEN @startDate AND @endDate
ORDER BY d.Item, d.dt
结果
| Item | dt | Sales |
|---------|------------|--------|
| tenant1 | 2014-01-01 | 100 |
| tenant1 | 2014-01-02 | 100 |
| tenant1 | 2014-01-03 | 100 |
| tenant1 | 2014-01-04 | NULL |
| tenant1 | 2014-01-05 | 100 |
| tenant2 | 2014-01-01 | 100 |
| tenant2 | 2014-01-02 | NULL |
| tenant2 | 2014-01-03 | NULL |
| tenant2 | 2014-01-04 | 100 |
| tenant2 | 2014-01-05 | NULL |
| tenant3 | 2014-01-01 | 100 |
| tenant3 | 2014-01-02 | NULL |
| tenant3 | 2014-01-03 | 100 |
| tenant3 | 2014-01-04 | NULL |
| tenant3 | 2014-01-05 | 100 |
我lovewewesthemenace回答!投票并保存以供将来参考。这是另一个建议。在 Crystal 的 SQL 命令中使用此代码。左外连接到您的 table 销售额。它的长。这很乏味。但是,如果您不是 SQL 专家,它更容易理解,这很好地描述了我 :) 如果您找到了他的答案并且有效,那么请忽略此解决方案。
注意:除了满足在这样的 SQL 语句中使用 from 子句的要求外,from 子句没有任何作用。如果 tblSales 很大 table 考虑使用您的数据库中的一个,它的数据较少并且仍然保留到 tblSales 的外部连接。
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','01') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','02') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','03') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','04') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','05') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','06') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','07') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','08') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','09') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','10') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','11') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','12') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','13') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','14') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','15') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','16') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','17') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','18') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','19') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','20') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','21') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','22') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','23') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','24') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','25') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','26') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','27') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','28') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','29') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','30') as DATE) as salesdate
from tblSales
UNION
select distinct
cast(concat(DATEPART(YYYY, GETDATE()),'-',DATEPART(MM,GETDATE()),'-','31') as DATE) as salesdate
from tblSales