为什么这两个查询的表现如此不同?

时间:2012-01-24 19:04:30

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

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

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

-- 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 Execution plant #1

查询#2 Execution plant #2

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

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

3 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:3)

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

  1. 全文搜索以解析CONTAINS(Name, @Filter)
  2. 索引扫描以查找匹配行的其他列
  3. 使用散列连接将两者结合起来
  4. concatenation operator形成两个记录集的并集。所以看起来第二个查询正在做:

    1. 索引扫描(后来用于查找其他列)
    2. 持续扫描。我假设它将您的查询视为未参数化,因此查询计划不必适用于@Filter的任何其他值。如果正确,则常量扫描会解析@Filter is not null
    3. 全文搜索以解析CONTAINS(Name, @Filter)
    4. 使用2
    5. 中的空集将3的结果联合起来
    6. loop连接1和4的结果以查找其他列
    7. 散列连接以内存换取速度;如果你的系统有足够的内存,它比循环连接快得多。这很容易解释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)
      

答案 2 :(得分:1)

您已经引入了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