【问题标题】:Why are the performances of these 2 queries so different?为什么这两个查询的性能如此不同?
【发布时间】:2012-02-18 00:52:11
【问题描述】:

我有一个使用全文索引搜索产品(250,000 行)的存储过程。

存储过程带一个参数,即全文搜索条件。这个参数可以为空,所以我添加了一个空检查,查询突然开始运行速度变慢了几个数量级。

-- This is normally a parameter of my stored proc
DECLARE @Filter VARCHAR(100)
SET @Filter = 'FORMSOF(INFLECTIONAL, robe)'

-- #1 - Runs < 1 sec
SELECT TOP 100 ID FROM dbo.Products
WHERE CONTAINS(Name, @Filter)

-- #2 - Runs in 18 secs
SELECT TOP 100 ID FROM dbo.Products
WHERE @Filter IS NULL OR CONTAINS(Name, @Filter)

以下是执行计划:

查询 #1

查询 #2

我必须承认我对执行计划不是很熟悉。对我来说唯一明显的区别是连接不同。我会尝试添加一个提示,但我的查询中没有加入我不知道该怎么做。

我也不太明白为什么要使用名为 IX_SectionID 的索引,因为它是一个只包含 SectionID 列的索引,而该列在任何地方都没有使用。

【问题讨论】:

    标签: sql-server performance sql-server-2008-r2 sql-execution-plan


    【解决方案1】:

    OR 可以碾压性能,所以这样做:

    DECLARE @Filter VARCHAR(100)
    SET @Filter = 'FORMSOF(INFLECTIONAL, robe)'
    
    IF @Filter IS NOT NULL
    BEGIN
        SELECT TOP 100 ID FROM dbo.Products
        WHERE CONTAINS(Name, @Filter)
    END
    ELSE
    BEGIN
        SELECT TOP 100 ID FROM dbo.Products
    END
    

    看这篇文章:Dynamic Search Conditions in T-SQL by Erland Sommarskog 和这个问题:SQL Server 2008 - Conditional Query

    【讨论】:

    • 不错的文章 - 添加OPTION (RECOMPILE) 实际上解决了第二个查询的性能问题(但是另一个问题是CONTAINS() 在参数为NULL 时会引发错误,但这是另一个问题)。跨度>
    【解决方案2】:

    您引入了 OR 条件。 在大多数情况下,显式检查 NULL 并执行一个查询与您的方法相比要快得多。

    例如试试这个:

    IF @Filter IS NULL
     BEGIN
    SELECT TOP 100 ID FROM dbo.Products
    END
    ELSE
    BEGIN
    SELECT TOP 100 ID FROM dbo.Products
    WHERE @Filter CONTAINS(Name, @Filter)
    END
    

    【讨论】:

      【解决方案3】:

      第一个查询计划看起来很简单:

      1. 全文搜索解析CONTAINS(Name, @Filter)
      2. 索引扫描以查找匹配行的其他列
      3. 使用哈希连接将两者结合起来

      concatenation operator 形成两个记录集的联合。所以看起来第二个查询正在执行:

      1. 索引扫描(后来用于查找其他列)
      2. 持续扫描。我假设它将您的查询视为未参数化,因此查询计划不必适用于@Filter 的任何其他值。如果正确,持续扫描会解析@Filter is not null
      3. 全文搜索解析CONTAINS(Name, @Filter)
      4. 将 3 的结果与 2 的空集联合
      5. 循环连接 1 和 4 的结果以查找其他列

      哈希连接以内存换取速度;如果您的系统有足够的内存,它比循环连接快得多。这可以很容易地解释 10-100 倍的减速。

      一种解决方法是使用两个不同的查询:

      if @Filter is null
          SELECT TOP 100 ID FROM dbo.Products
      else
          SELECT TOP 100 ID FROM dbo.Products WHERE CONTAINS(Name, @Filter)
      

      【讨论】:

      • 有趣 - 有没有办法在第二个查询中强制使用哈希连接?
      • 是的,您可以使用inner hash join 重写它,但这会相当复杂。最好使用 Sommarskog 文章中的一种解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-11
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      • 2017-11-19
      • 1970-01-01
      • 2023-03-10
      相关资源
      最近更新 更多