当我运行以下代码时,它需要22.5分钟的时间,并且读取1.06亿。但是,如果我自己仅运行内部select语句,则只需15秒,并且可以读取264k。附带说明,select查询不返回任何记录。

知道为什么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


评论

为了避免出现行进目标问题,另一个想法(想知道,请注意!)可能是尝试相反的操作-如果不存在(...)开始结束否则开始<执行某些操作>结束。

#1 楼


知道为什么IF EXISTS可以运行更长的时间并进行更多的读取吗?我还更改了select语句以执行SELECT TOP 1 [dlc].[id],并在2分钟后将其杀死。

正如我在对以下相关问题的回答中所解释的那样:
TOP如何(以及为什么)影响执行力?计划吗?
使用EXISTS引入了一个行目标,优化器产生了一个执行计划,旨在快速定位第一行。在此,假定数据是均匀分布的。例如,如果统计数据显示在100,000行中有100个预期的匹配项,则将假定它仅需读取1,000行即可找到第一个匹配项。
如果此假设成立,这将导致比预期的执行时间更长的时间是错误的。例如,如果SQL Server选择的访问方法(例如无序扫描)恰好在搜索中很晚才找到第一个匹配值,则可能导致几乎完整的扫描。另一方面,如果恰好在前几行中找到匹配的行,则性能将非常好。这是行目标的基本风险-不一致的性能。

作为临时解决方案,我将其更改为执行count(*)并将该值分配给变量通常可以重新构造查询,以便不分配行目标。如果没有行目标,则遇到第一个匹配行(如果写正确)时,查询仍然可以终止,但是执行计划策略可能会有所不同(并且希望更有效)。显然,count(*)将需要读取所有行,因此这不是一个完美的选择。
如果您正在运行SQL Server 2008 R2或更高版本,则通常还可以使用记录并受支持的跟踪标志4138来获取执行。没有连续目标的计划。也可以使用受支持的提示OPTION (QUERYTRACEON 4138)来指定此标志,但是请注意,除非有计划指南使用,否则它需要运行时sysadmin权限。
不幸的是,
以上条件均不能通过IF EXISTS条件语句起作用。它仅适用于常规DML。它将与您尝试过的其他SELECT TOP (1)配方一起使用。如前所述,这可能比使用COUNT(*)更好,因为q4312079q必须对所有合格的行进行计数。
话说,有很多方法可以表达此要求,从而使您能够避免或控制行目标,同时尽早终止搜索。最后一个例子:
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;


评论


您提供的替代示例运行了3.75分钟,执行了46m读取。因此,虽然比原始查询要快,但我认为在这种情况下,我会坚持使用@cnt = count(*)并随后评估变量。特别是由于99%的时间都在运行,因此其中没有任何内容。听起来,根据您和Rob的回答,只有当您确实希望某种结果并且该结果均匀地分布在数据中时,Exists存在才是好的。

–克里斯·伍兹(Chris Woods)
16年1月15日在17:52

@ChrisWoods:您说过:“尤其是因为运行此程序的99%的时间中没有任何内容”。这几乎可以确保一个行的目标不是一个好主意,因为您希望通常没有行,并且必须扫描所有内容以查找没有行。如果您无法添加一些聪明的索引,请坚持使用COUNT(*)。

–罗斯压机
16年1月20日在15:57

#2 楼

因为EXISTS只需要查找一行,所以它将使用一个行目标。有时这可能会产生不理想的计划。如果您希望这样,请使用COUNT(*)的结果填充变量,然后测试该变量以查看其是否大于0。

......目标行很小它将避免阻塞操作,例如构建哈希表或对合并联接有用的排序流,因为它将确定它一定会很快找到某些内容,因此,如果找到某些内容,嵌套循环将是最好的选择。除此之外,这会使整个计划变得更加糟糕。如果查找单个行很快,那么您希望使用这种方法来避免出现块...