问题描述
我遇到了一个问题,我们不能在视图中使用底层查询,而只能在表中使用。不幸的是,我们处理的情况是我们没有表作为该项目的选项。
我很好奇是否有人知道我应该寻找哪个方向来替代底层逻辑:
我尝试做的是通过执行以下查询为日期范围内的每一天创建一条记录:
WITH CTE_PerDay AS (
SELECT
TableDaterange.objectId,TableDaterange.amount,TableDaterange.beginDate,COALESCE(TableDaterange.endDate,'2099-12-31') AS endDate
FROM TableDaterange
UNION ALL
SELECT
CTE_PerDay.objectId,CTE_PerDay.amount,DATEADD(DAY,1,CTE_PerDay.beginDate) AS beginDate,CTE_PerDay.endDate
FROM CTE_PerDay
WHERE GETDATE() > DATEADD(DAY,CTE_PerDay.beginDate)
)
SELECT * FROM CTE_PerDay
OPTION (maxrecursion 0)
样本数据集 TableDatarange
对象 ID | 金额 | 开始日期 | 结束日期 |
---|---|---|---|
1 | 500 | 2020-01-03 | |
2 | 35 | 2015-05-31 | 2019-10-01 |
3 | 200 | 2017-03-15 | 2020-06-02 |
CREATE TABLE TableDaterange
(
ObjectId varchar(300),Amount int,beginDate date,endDate date
);
INSERT INTO TableDaterange ( ObjectId,Amount,beginDate,endDate )
VALUES
('1',500,'2020-01-03',NULL),('2',35,'2015-05-31','2019-10-01'),('3',200,'2017-03-15','2020-06-02');
因此查询运行良好,但是在视图中我无法使用 OPTION
功能,如果没有它,我会收到错误“语句终止。最大递归 100 在语句完成之前已用完。
有什么建议吗?
解决方法
您可以使用 Tally:这是一个基于集合的解决方案,当迭代次数增加时,它的性能比递归更好 - 并且在视图中受支持。
这是一种方法:
select t.objectid,t.amount,dateadd(day,x.n,t.begindate) as dt
from (
select row_number() over (order by (select null)) - 1
from (values(0),(0),(0)) a(n)
cross join (values(0),(0)) b(n)
cross join (values(0),(0)) c(n)
) x(n)
inner join tabledatarange t
on dateadd(day,t.begindate) <= case
when enddate <= convert(date,getdate()) then enddate
else convert(date,getdate())
end
计数生成 0 到 999 之间的所有数字(您可以通过添加 cross join
轻松扩展它)。我们用它来“乘”原始表的行并生成日期范围。
我试图重写处理结束日期的部分。我知道您不想要未来的日期,因此 on
子句中的条件就是这样做的。
对于此示例数据:
ObjectId | Amount | beginDate | endDate -------: | -----: | :--------- | :--------- 1 | 500 | 2020-12-28 | null 2 | 35 | 2019-09-26 | 2019-10-01 3 | 200 | 2020-05-28 | 2020-06-02
查询返回:
objectid | amount | dt -------: | -----: | :--------- 1 | 500 | 2020-12-28 1 | 500 | 2020-12-29 1 | 500 | 2020-12-30 1 | 500 | 2020-12-31 2 | 35 | 2019-09-26 2 | 35 | 2019-09-27 2 | 35 | 2019-09-28 2 | 35 | 2019-09-29 2 | 35 | 2019-09-30 2 | 35 | 2019-10-01 3 | 200 | 2020-05-28 3 | 200 | 2020-05-29 3 | 200 | 2020-05-30 3 | 200 | 2020-05-31 3 | 200 | 2020-06-01 3 | 200 | 2020-06-02,
您提出的问题的答案是:将 OPTION
添加到外部查询。视图只是一个子查询,因此不能包含查询级别的提示。
您没有问的问题是:这实际上是日历表的最佳方法吗?
答案是:不。在 CTE 中递归 28854 次对性能非常不利。
在磁盘上有一个日历表更好,或者使用 Itzik Ben-Gan 的 tally table(我可能会在表值函数中这样做):
CREATE FUNCTION dbo.GetDates
( @startDate as DateTime,@endDate as DateTime )
RETURNS TABLE WITH SCHEMABINDING
AS RETURN
(
WITH
L0 AS ( SELECT 1 AS c FROM (VALUES(1),(1)) AS D(c) ),L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),L4 AS ( SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B ),L5 AS ( SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B ),Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
FROM L5 )
SELECT TOP (DATEDIFF(day,@startDate,@endDate) + 1)
DATEADD(day,rownum,'1999-12-31')
FROM Nums
);