我有一个表,该表包含以下列:DeskID *,ProductID *,Date *,Amount(其中带有*标记的列为主键)。所使用的产品会随时间变化,如下图所示。
左侧为表格格式,右侧为一张桌子(希望)直观地表示了数据
目标是在一个日期范围内按办公桌和日期获取最新产品的总和,包括不再使用的产品。
例如使用所需表格上方的数据是:
所以在1月1日,总和为产品A的1
1月2日,总和是A的2和B的5,所以7
1月4日,总和是A的1(已停用,因此请从3号取值),B的5和C的2,因此总计8
等
我尝试使用桌面上的分区和按日期排序的产品来获取最新值,并使用@date Date参数将以下代码转换为函数(下面的Function1)
select @date 'Date', t.DeskID, SUM(t.Amount) 'Sum' from (
select @date 'Date', t.DeskID, t.ProductID, t.Amount
, row_number() over (partition by t.DeskID, t.ProductID order by t.Date desc) as roworder
from Table1 t
where 1 = 1
and t.Date <= @date
) t
where t.roworder = 1
group by t.DeskID
然后使用实用程序日历表并交叉应用以在如下所示的时间范围内获取所需的值
select * from Calendar c
cross apply Function1(c.CalendarDate)
where c.CalendarDate >= '20190101' and c.CalendarDate <= '20191009'
这具有预期的结果,但是太慢了。目前,每个办公桌使用大约50种产品,并且每个月都会滚动产品,因此,在仅5年的时间里,每个办公桌都有3000多种产品的历史,这使整个事情停滞了。 (一个月的时间范围大约为30秒)
有更好的方法吗?
答案 0 :(得分:0)
将功能更改为以下速度应该更快:
select @date 'Date', t.DeskID, SUM(t.Amount) 'Sum'
FROM (SELECT m.DeskID, m.ProductID, MAX(m.[Date) AS MaxDate
FROM Table1 m
where m.[Date] <= @date) d
INNER JOIN Table1 t
ON d.DeskID=t.DeskID
AND d.ProductID=t.ProductID
and t.[Date] = d.MaxDate
group by t.DeskID
答案 1 :(得分:0)
TVF的性能通常会下降。以下内容将完全删除TVF:
-- DROP TABLE Table1;
CREATE TABLE Table1 (DeskID int not null, ProductID nvarchar(32) not null, [Date] Date not null, Amount int not null, PRIMARY KEY ([Date],DeskID,ProductID));
INSERT Table1(DeskID,ProductID,[Date],Amount)
VALUES (1,'A','2019-01-01',1),(1,'A','2019-01-02',2),(1,'B','2019-01-02',5),(1,'A','2019-01-03',1)
,(1,'B','2019-01-03',4),(1,'C','2019-01-03',3),(1,'B','2019-01-04',5),(1,'C','2019-01-04',2),(1,'C','2019-01-05',2)
GO
DECLARE @StartDate date=N'2019-01-01';
DECLARE @EndDate date=N'2019-01-05';
;WITH cte_p
AS
(
SELECT DISTINCT DeskID,ProductID
FROM Table1
WHERE [Date] <= @EndDate
),
cte_a
AS
(
SELECT @StartDate AS [Date], p.DeskID, p.ProductID, ISNULL(a.Amount,0) AS Amount
FROM (
SELECT t.DeskID, t.ProductID
, MAX(t.Date) AS FirstDate
FROM Table1 t
WHERE t.Date <= @StartDate
GROUP BY t.DeskID, t.ProductID) f
INNER JOIN Table1 a
ON f.DeskID=a.DeskID
AND f.ProductID=a.ProductID
AND f.[FirstDate]=a.[Date]
RIGHT JOIN cte_p p
ON p.DeskID=a.DeskID
AND p.ProductID=a.ProductID
UNION ALL
SELECT DATEADD(DAY,1,a.[Date]) AS [Date], t.DeskID, t.ProductID, t.Amount
FROM Table1 t
INNER JOIN cte_a a
ON t.DeskID=a.DeskID
AND t.ProductID=a.ProductID
AND t.[Date] > a.[Date]
AND t.[Date] <= DATEADD(DAY,1,a.[Date])
WHERE a.[Date]<@EndDate
UNION ALL
SELECT DATEADD(DAY,1,a.[Date]) AS [Date], a.DeskID, a.ProductID, a.Amount
FROM cte_a a
WHERE NOT EXISTS(SELECT 1 FROM Table1 t
WHERE t.DeskID=a.DeskID
AND t.ProductID=a.ProductID
AND t.[Date] > a.[Date]
AND t.[Date] <= DATEADD(DAY,1,a.[Date]))
AND a.[Date]<@EndDate
)
SELECT [Date], DeskID, SUM(Amount)
FROM cte_a
GROUP BY [Date], DeskID;