任何想法为什么IF EXISTS会让它运行得更长并且读取更多?我还将select语句改为SELECT TOP 1 [dlc].[id],我在2分钟后杀了它.
作为临时修复,我将其更改为count(*)并将该值赋给变量@cnt.然后它做IF 0<>. @cnt声明.但我认为EXISTS会更好,因为如果在select语句中返回了记录,它会在找到至少一条记录后停止执行扫描/搜索,而count(*)将完成整个查询.我错过了什么?
IF EXISTS (SELECT [dlc].[ID] FROM TableDLC [dlc] JOIN TableD [d] ON [d].[ID] = [dlc].[ID] JOIN TableC [c] ON [c].[ID] = [d].[ID2] WHERE [c].[Name] <> [dlc].[Name]) BEGIN <do something> END
解决方法
Any idea why the
IF EXISTS
would make it run so much longer and do so many more reads? I also changed the select statement to doSELECT TOP 1 [dlc].[id]
and I killed it after 2 minutes.
正如我在回答这个相关问题时解释的那样:
How (and why) does TOP impact an execution plan?
使用EXISTS引入了一个行目标,优化器生成一个旨在快速定位第一行的执行计划.在此过程中,它假定数据是均匀分布的.例如,如果统计信息显示100,000行中有100个预期匹配项,则假设它必须只读取1,000行才能找到第一个匹配项.
如果这个假设结果有问题,这将导致执行时间超过预期.例如,如果sql Server选择在搜索中很晚就找到第一个匹配值的访问方法(例如无序扫描),则可能导致几乎完全扫描.另一方面,如果在前几行中发现匹配的行,则性能将非常好.这是行目标的基本风险 – 性能不一致.
As a temporary fix I have changed it to do a count( * ) and assign that value to a variable
通常可以重新构造查询,以便不分配行目标.如果没有行目标,查询仍然可以在遇到第一个匹配行时终止(如果写得正确),但执行计划策略可能会有所不同(希望更有效).显然,count(*)将需要读取所有行,因此它不是一个完美的替代方案.
如果您运行的是sql Server 2008 R2或更高版本,则通常也可以使用documented and supported trace flag 4138来获取没有行目标的执行计划.也可以使用supported hint OPTION(QUERYTRACEON 4138)指定此标志,但要注意它需要运行时sysadmin权限,除非与计划指南一起使用.
不幸
以上都不适用于IF EXISTS条件语句.它仅适用于常规DML.它将与您尝试的备用SELECT TOP(1)配方一起使用.这可能比使用COUNT(*)更好,后者必须计算所有限定行,如前所述.
也就是说,有许多方法可以表达此要求,这将允许您避免或控制行目标,同时尽早终止搜索.最后一个例子:
DECLARE @Exists bit; SELECT @Exists = CASE WHEN EXISTS ( SELECT [dlc].[ID] FROM TableDLC [dlc] JOIN TableD [d] ON [d].[ID] = [dlc].[ID] JOIN TableC [c] ON [c].[ID] = [d].[ID2] WHERE [c].[Name] <> [dlc].[Name] ) THEN CONVERT(bit,1) ELSE CONVERT(bit,0) END OPTION (QUERYTRACEON 4138); IF @Exists = 1 BEGIN ... END;