sql-server - 在查询周围添加条件会使时间增加 2400% 以上

标签 sql-server

更新:我会尽快获得查询计划。

我们有一个性能不佳的查询,对于特定组织来说需要 4 分钟。在通常的重新编译存储过程和更新统计信息没有帮助之后,我们将 if Exists(...) 重写为 select count(*)... 并将存储过程从 4 分钟写入到 70 毫秒。使 70 毫秒的查询需要 4 分钟的条件有什么问题?查看示例

这些都需要 4 分钟以上:

if (
  SELECT COUNT(*)       
    FROM ObservationOrganism  omo
    JOIN Observation          om  ON  om.ObservationID  = omo.ObservationMicID
    JOIN Organism             o   ON  o.OrganismID      = omo.OrganismID
    JOIN ObservationMicDrug   omd ON  omd.ObservationOrganismID = omo.ObservationOrganismID
    JOIN SIRN                 srn ON  srn.SIRNID        = omd.SIRNID
    JOIN OrganismDrug         od  ON  od.OrganismDrugID = omd.OrganismDrugID
  WHERE
    om.StatusCode IN ('F', 'C')
    AND o.OrganismGroupID <> -1
    AND od.OrganismDrugGroupID <> -1
    AND (om.LabType <> 'screen' OR om.LabType IS NULL)) > 0

print 'records';       

-

IF (EXISTS(
  SELECT *       
    FROM ObservationOrganism  omo
    JOIN Observation          om  ON  om.ObservationID  = omo.ObservationMicID
    JOIN Organism             o   ON  o.OrganismID      = omo.OrganismID
    JOIN ObservationMicDrug   omd ON  omd.ObservationOrganismID = omo.ObservationOrganismID
    JOIN SIRN                 srn ON  srn.SIRNID        = omd.SIRNID
    JOIN OrganismDrug         od  ON  od.OrganismDrugID = omd.OrganismDrugID
  WHERE
    om.StatusCode IN ('F', 'C')
    AND o.OrganismGroupID <> -1
    AND od.OrganismDrugGroupID <> -1
    AND (om.LabType <> 'screen' OR om.LabType IS NULL))

print 'records'

这一切都需要 70 毫秒:

Declare @recordCount INT;
SELECT @recordCount = COUNT(*)       
    FROM ObservationOrganism  omo
    JOIN Observation          om  ON  om.ObservationID  = omo.ObservationMicID
    JOIN Organism             o   ON  o.OrganismID      = omo.OrganismID
    JOIN ObservationMicDrug   omd ON  omd.ObservationOrganismID = omo.ObservationOrganismID
    JOIN SIRN                 srn ON  srn.SIRNID        = omd.SIRNID
    JOIN OrganismDrug         od  ON  od.OrganismDrugID = omd.OrganismDrugID
  WHERE
    om.StatusCode IN ('F', 'C')
    AND o.OrganismGroupID <> -1
    AND od.OrganismDrugGroupID <> -1
    AND (om.LabType <> 'screen' OR om.LabType IS NULL);

IF(@recordCount > 0)
  print 'records';

我不明白为什么将完全相同的 Count(*) 查询移动到 if 语句中会导致这种降级,或者为什么“Exists”比 Count 慢>。我什至在 select CASE WHEN Exists() 中尝试了 exists(),但仍然需要 4 分钟以上。

最佳答案

鉴于我之前的回答已被提及,我将尝试再次解释,因为这些事情非常棘手。所以是的,我认为您遇到了与 the other question 相同的问题。即row goal问题。

因此,为了尝试解释导致此问题的原因,我将从引擎可以使用的三种类型的联接开始(并且几乎是明确的):循环联接、合并联接、哈希联接。循环连接顾名思义,是两组数据上的嵌套循环。合并连接采用两个排序列表并以锁定步骤在它们之间移动。散列连接将较小集合中的所有内容放入文件柜中,然后在文件柜装满后查找较大集合中的项目。

因此,就性能而言,循环连接几乎不需要任何设置,如果您只寻找少量数据,它们确实是最佳选择。就任何数据大小的连接性能而言,合并是最好的,但要求数据已经排序(这种情况很少见)。哈希连接需要大量的设置,但允许快速连接大型数据集。

现在我们来了解您的查询以及 COUNT(*)EXISTS/TOP 1 之间的区别。因此,您看到的行为是优化器认为此查询的行确实很可能(您可以通过规划查询而不进行分组并查看它认为在最后一步中将获得多少记录来确认这一点)。特别是,它可能认为对于该查询中的某个表,该表中的每条记录都会出现在输出中。

“ Eureka !”它说,“如果此表中的每一行最终都出现在输出中,为了查找是否存在,我可以在整个过程中进行非常便宜的启动循环连接,因为即使对于大型数据集来说速度很慢,但我只需要一行。”但随后它找不到该行。并且再也找不到了。现在,它正在使用现有的效率最低的方法来迭代大量数据,以清除大量数据。

相比之下,如果您要求完整的数据计数,它必须根据定义找到每条记录。它看到大量数据,并选择最适合迭代整个数据集而不是其中一小部分的选择。

另一方面,如果它确实是正确的,并且记录之间的相关性非常好,那么它就会以尽可能少的服务器资源找到您的记录,并最大化其整体吞吐量。

关于sql-server - 在查询周围添加条件会使时间增加 2400% 以上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34662081/

相关文章:

sql - 带有 like 子句的 Unicode 谓词 ( N'%ש֩%' ) 返回所有行

mysql - 在链接的 MySQL 服务器中的 6 个表中的 3 个上查询时出错

SQL Server : setting language while creating view

sql server 显示缺失日期

sql-server - 创建带有主键的 View ?

sql - 将多行的值汇总到一个新列中

sql - ")x"在查询中意味着什么?

c# - 为什么 Entity Framework 会尝试选择所有列,即使我只指定了两个列?

sql - 如何使用 SQL Server 时态表识别更改的值?

sql - 仅当源数据不为空且不同时才更新目标表