Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server SQL server如何评估包含用户定义函数的执行计划的成本?_Sql Server_Sql Server 2005_User Defined Functions_Sql Execution Plan - Fatal编程技术网

Sql server SQL server如何评估包含用户定义函数的执行计划的成本?

Sql server SQL server如何评估包含用户定义函数的执行计划的成本?,sql-server,sql-server-2005,user-defined-functions,sql-execution-plan,Sql Server,Sql Server 2005,User Defined Functions,Sql Execution Plan,我有一个存储过程,它根据DATEADD函数的结果进行过滤-我的理解是,这类似于使用用户定义的函数,因为SQL server无法根据该函数的输出存储统计信息,因此无法评估执行计划的成本 查询看起来有点像这样: SELECT /* Columns */ FROM TableA JOIN TableB ON TableA.id = TableB.join_id WHERE DATEADD(hour, TableB.HoursDifferent, TableA.StartDate) <= @Now

我有一个存储过程,它根据DATEADD函数的结果进行过滤-我的理解是,这类似于使用用户定义的函数,因为SQL server无法根据该函数的输出存储统计信息,因此无法评估执行计划的成本

查询看起来有点像这样:

SELECT /* Columns */ FROM
TableA JOIN TableB
ON TableA.id = TableB.join_id
WHERE DATEADD(hour, TableB.HoursDifferent, TableA.StartDate) <= @Now
因此不可能预先计算DATEADD的结果

我看到的是一个糟糕的执行计划,我认为这是由于SQL server错误地将从树的某个部分返回的行数估计为1,而实际上是65000行。然而,当数据库中存在不同的数据时,我看到相同的存储过程在很短的时间内执行

我的问题是——在这种情况下,查询优化程序如何估计函数的结果


更新:仅供参考,我更感兴趣的是理解为什么有些时候我得到了一个好的执行计划,而其余时间我没有-我已经非常清楚如何在长期内解决这个问题。

这将有助于了解功能,但我看到的一件事是,在查询中嵌入这样的函数可能会导致性能低下。如果你能事先评估一下,你的状态可能会更好。例如,代替

WHERE MyDate < GETDATE()
试一试


这似乎表现更好

这里的问题不是计划的成本。列上的函数阻止SQL执行索引查找。您将得到索引扫描或表扫描

我的建议是看看你是否能从函数中得到一列,基本上看你是否能把函数移到等式的另一边。这并不完美,但这意味着至少有一列可以用于索引查找

类似于这个粗略的想法,没有使用TableB.HoursDifference上的索引进行测试,然后是TableA中join列上的索引

DATEDIFF(hour, @Now, TableA.StartDate) >= TableB.HoursDifferent
在成本方面,我怀疑乐观主义者会使用表中30%的“拇指吸吮”,因为它无法使用统计数据得到准确的估计,而且这是一种不平等。这意味着它将猜测表的30%将由该谓词返回

在没有看到执行计划的情况下,很难确定地说什么。您提到的估计值为1行,实际值为65000行。在某些情况下,这根本不是问题。 @Kragen

简短回答:如果您正在使用十个表进行查询,请习惯它。您需要学习所有关于查询提示的知识,以及更多的技巧

长答覆:

SQL server通常只为大约三到五个表生成优秀的查询计划。根据我的经验,一旦你超出了这个范围,你基本上必须自己编写查询计划,使用所有的索引和连接提示。此外,标量函数的估计值似乎为Cost=0,这简直是疯了

原因是在那之后就太复杂了。查询优化人员必须决定在算法上做什么,而且SQL Server团队中即使是最聪明的天才也有太多可能的组合,无法创建一个真正通用的算法

他们说乐观主义者比你聪明。这可能是真的。但你有一个优势。这样做的好处是,如果它不起作用,你可以扔掉它,再试一次!到第六次尝试时,如果您知道数据,您应该已经有了一些可以接受的东西,即使是十表联接。查询优化者无法做到这一点,它必须立即拿出某种计划,而且不会有第二次机会

我最喜欢的技巧是通过将where子句转换为case语句来强制执行其顺序。而不是:

WHERE
predicate1
AND predicate2
AND....
使用以下命令:

WHERE
case 
when not predicate1 then 0
when not predicate2 then 0
when not .... then 0
else 1 end = 1

将谓词从最便宜的顺序排列到最昂贵的顺序,您将得到逻辑上相同但SQL server无法处理的结果—它必须按照您所说的顺序进行操作。

DATEADD不是用户定义的函数。内置系统函数的处理方式通常与用户定义函数不同。它确实在进行表/索引扫描,但每个表只有少量条目,例如600个左右-问题的原因是SQL server最终对一个仅包含600行的表进行了65000次RDI查找!。再次抱歉,我不能向您展示一个执行计划,但是如果不了解整个上下文,它就没有多大意义,就像我说的,它涉及10个不同的表、一个250行的存储过程和大量的索引。因为SQL无法准确地估计列在函数中时受影响的行数。当列不在函数中时,它可以使用列统计信息来获得相当好的估计值
WHERE
case 
when not predicate1 then 0
when not predicate2 then 0
when not .... then 0
else 1 end = 1