我有一个视图,使用CTE从表中返回2个整数。如果我查询这样的视图,它会在不到一秒的时间内运行
SELECT * FROM view1 WHERE ID = 1
但是如果我查询这样的视图需要4秒钟。
DECLARE @id INT = 1
SELECT * FROM View1 WHERE ID = @id
我检查了2个查询计划,第一个查询正在主表上执行Clustered index seek返回1条记录,然后将其余的视图查询应用于该结果集,其中第二个查询执行索引扫描返回大约3000条记录而不仅仅是我感兴趣的记录,然后过滤结果集。
我是否有任何明显的缺失,试图让第二个查询使用Index Seek而不是索引扫描。我正在使用SQL 2008,但我所做的任何事情都需要在SQL 2005上运行。起初我认为这是某种参数嗅探问题,但即使清除缓存,我也会得到相同的结果。
答案 0 :(得分:36)
可能是因为在参数的情况下,优化器无法知道该值不为null,因此它需要创建一个计划,即使它返回正确的结果。如果您有SQL Server 2008 SP1,则可以尝试将OPTION(RECOMPILE)
添加到查询中。
答案 1 :(得分:4)
您可以为查询添加hint的OPTIMIZE,例如
DECLARE @id INT = 1
SELECT * FROM View1 WHERE ID = @id OPTION (OPTIMIZE FOR (@ID = 1))
答案 2 :(得分:3)
在我的情况下,DB表列类型被定义为VarChar,并且参数化查询参数类型被定义为NVarChar,这在实际执行计划中引入了CONVERT_IMPLICIT
以匹配数据类型,这是母猪的罪魁祸首表现,2秒vs 11秒。只需更正参数类型,使参数化查询与非参数化版本一样快。
希望这可以帮助有类似问题的人。
答案 3 :(得分:1)
当SQL开始使用变量优化查询的查询计划时,它将匹配列的可用索引。在这种情况下有一个索引,所以SQL认为它只会扫描索引寻找值。当SQL使用列和文字值制定查询计划时,它可以查看统计信息和值,以确定它是否应扫描索引或查找是否正确。
使用优化提示和值告诉SQL“这是将在大多数情况下使用的值,因此优化此值”,并存储计划,就好像使用了这个文字值一样。使用优化提示和UNKNOWN的子提示告诉SQL您不知道该值是什么,因此SQL查看列的统计信息并决定什么,搜索或扫描最佳,并相应地制定计划。
答案 4 :(得分:1)
我自己遇到了这个问题,并且运行了<直接分配10ms( WHERE UtilAcctId = 12345 ),但是通过变量赋值( WHERE UtilAcctId = @UtilAcctId )占用了100多倍。
后者的执行计划与我在整个表上运行视图没有什么不同。
我的解决方案不需要大量索引,优化程序提示或长统计信息更新。
相反,我将视图转换为 用户表函数 ,其中参数是WHERE子句所需的值。事实上,这个WHERE子句嵌套了3个深度查询,它仍然有效,并且它回到了< 10ms的速度。
最后我将参数更改为TYPE,它是UtilAcctIds(int)的表。然后我可以将WHERE子句限制为表中的列表。 WHERE UtilAcctId = [parameter-List] .UtilAcctId。 这样做效果更好。我认为用户表函数是预编译的。
答案 5 :(得分:0)
我自己遇到了同样的问题,结果发现这是一个缺少的索引,涉及对子查询结果的(左)连接。
select *
from foo A
left outer join (
select x, count(*)
from bar
group by x
) B on A.x = B.x
为bar.x添加了名为bar_x的索引
答案 6 :(得分:0)
DECLARE @id INT = 1
这样做
DECLARE @sql varchar(max)
SET @sql =' SELECT * FROM View1 WHERE ID =' + CAST(@id as varchar)
EXEC(@sql)
解决您的问题
答案 7 :(得分:0)
我知道这已经很久了,但是我遇到了同样的问题,并且有一个非常简单的解决方案,不需要提示,统计信息更新,其他索引,强制计划等。
基于以上评论“优化器无法知道该值不为null”,我决定将这些值从变量移到表中:
原始代码:
declare @StartTime datetime2(0) = '10/23/2020 00:00:00'
declare @EndTime datetime2(0) = '10/23/2020 01:00:00'
SELECT * FROM ...
WHERE
C.CreateDtTm >= @StartTime
AND C.CreateDtTm < @EndTime
新代码:
declare @StartTime datetime2(0) = '10/23/2020 00:00:00'
declare @EndTime datetime2(0) = '10/23/2020 01:00:00'
CREATE TABLE #Times (StartTime datetime2(0) NOT NULL, EndTime datetime2(0) NOT NULL)
INSERT INTO #Times(StartTime, EndTime) VALUES(@StartTime, @EndTime)
SELECT * FROM ...
WHERE
C.CreateDtTm >= (SELECT MAX(StartTime) FROM #Times)
AND C.CreateDtTm < (SELECT MAX(EndTime) FROM #Times)
这是立即执行的,而不是原始代码的几分钟(显然,结果可能会有所不同)。
我假设如果将主表中的数据类型更改为NOT NULL,它也可以正常工作,但是由于系统限制,我目前无法对此进行测试。