为什么 "where "子句中的标量值函数会变慢?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么 "where "子句中的标量值函数会变慢?相关的知识,希望对你有一定的参考价值。
遗憾的是,我不能准备一个例子让你在电脑上重复,但我认为这不是了解问题所在的必要条件.有这样的疑问。
with cte as (
select selectStations.ReceiverUID, 1907 as id
from WF4_Routes r WITH (NOLOCK)
inner join WF4_Stages selectStages WITH (NOLOCK) on selectStages.RouteID = r.ID and r.SentToCAS = 1 and r.PRUZ <> 1 and selectStages.PRUZ <> 1 and selectStages.StageType = 1
inner join WF4_Stations selectStations WITH (NOLOCK) on selectStations.ApprovalStageID = selectStages.ID and selectStations.PRUZ <> 1
)
select *--case when dbo.fnGetGkExchangeParticipantQuick(cte.ReceiverUID, id) = 'E477B8FA-7539-4B43-8961-807A29FECFC0' then 1 else 0 end
from cte
where dbo.fnGetGkExchangeParticipantQuick(cte.ReceiverUID, id) = 'E477B8FA-7539-4B43-8961-807A29FECFC0'
从整个脚本来看,在 "where "条件下调用标量函数的时刻 和在 "select "中注释出来的调用是很重要的。
CREATE FUNCTION [dbo].fnGetGkExchangeParticipantQuick(@CurrentUID uniqueidentifier, @IsExchangeParticipantAttrID int)
RETURNS uniqueidentifier
AS
BEGIN
declare @UnitUID uniqueidentifier
select @UnitUID = UID from GL_OBJECTS where UID = @CurrentUID and ACTIVE = 1
while @UnitUID is not null
begin
if (select top 1 cast(PropertyValue as bit) from MB_ATTRIBUTES_TO_VALUES where Object_UID = @UnitUID and Attribute_ID = @IsExchangeParticipantAttrID) = 1
break;
set @UnitUID = null
select @UnitUID = PARENT from GL_OBJECTS where UID = @UnitUID and ACTIVE = 1
end
return @UnitUID
END
GO
该函数从对象的层次结构中的父对象中搜索一个特定的属性。
如果我在 "where "条件下调用这个函数,那么查询将在1秒内执行,而从磁盘中读取的记录将达到60万条左右。如果我在 "select "条件下调用这个函数,那么这个函数将在10毫秒内执行,从磁盘上读取的数量为30条。
如果你看一下计划,你可以看到,由于某种原因,调度器用 "where "条件满足了 "with "条件,也就是说,他是想优化组合条件的执行,我不需要。我试着在 "with "条件或 "different "条件中加入分组,都没有用。
请帮我理解问题出在哪里?
cte往往不过是语法上的糖,SQL Server将整个查询组合成一个它认为最好的执行计划。
有时你可以通过将CTE中的函数调用添加为一列,然后在你的 where
条款。
with cte as (
select selectStations.ReceiverUID
, 1907 as id
, dbo.fnGetGkExchangeParticipantQuick(selectStations.ReceiverUID, 1907) as ExchangeParticipant
from WF4_Routes r WITH (NOLOCK)
inner join WF4_Stages selectStages WITH (NOLOCK) on selectStages.RouteID = r.ID and r.SentToCAS = 1 and r.PRUZ <> 1 and selectStages.PRUZ <> 1 and selectStages.StageType = 1
inner join WF4_Stations selectStations WITH (NOLOCK) on selectStations.ApprovalStageID = selectStages.ID and selectStations.PRUZ <> 1
)
select *
from cte
where ExchangeParticipant = 'E477B8FA-7539-4B43-8961-807A29FECFC0';
注意事项。
您永远不应该在 where
子句,因为不能使用索引,而且经常需要进行全表扫描。
而如果需要强制先执行查询的CTE部分,那么可以将结果具体化到一个临时表中,然后从中选择。这样SQL Server就创建了两个独立的执行计划。
以上是关于为什么 "where "子句中的标量值函数会变慢?的主要内容,如果未能解决你的问题,请参考以下文章
如何在使用 Linq 的 Where 子句之后选择数组索引?
为啥我在 View SELECT 查询中收到带有 WHERE 子句的“未知列 'xyz'”?