为什么这两个查询的性能如此不同?

发布于 2024-12-29 06:13:58 字数 777 浏览 1 评论 0原文

我有一个使用全文索引搜索产品(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 执行工厂 #1

查询 #2 Execution plant #2

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

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

I have a stored proc that searches for products (250,000 rows) using a full text index.

The stored proc takes a parameter that is the full text search condition. This parameter can be null, so I added a null check and the query suddenly started running orders of magnitude slower.

-- 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)

Here are the execution plans:

Query #1
Execution plant #1

Query #2
Execution plant #2

I must admit that I am not very familiar with execution plans. The only obvious difference to me is that the joins are different. I would try adding a hint but having no join in my query I am not sure how to do that.

I also do not quite understand why the index called IX_SectionID is used, since it is an index that only contains the column SectionID and that column is not used anywhere.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

倾听心声的旋律 2025-01-05 06:13:58

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

看看这篇文章:Erland Sommarskog 的 T-SQL 中的动态搜索条件 和这个问题:SQL Server 2008 -条件查询

OR can crush performance, so do it this way:

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

Look at this article: Dynamic Search Conditions in T-SQL by Erland Sommarskog and this question: SQL Server 2008 - Conditional Query.

笑着哭最痛 2025-01-05 06:13:58

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

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

连接运算符形成两个记录集的并集。所以看起来第二个查询正在执行:

  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)

The first query plan looks straightforward:

  1. a full text search to resolve CONTAINS(Name, @Filter)
  2. an index scan to look up the other columns of the matched rows
  3. combine the two using a hash join

The concatenation operator forms a union of two recordsets. So it looks like the second query is doing:

  1. an index scan (later used to look up other columns)
  2. a constant scan. I assume it's treating your query as not parameterized, so the query plan doesn't have to work for any other value of @Filter. If correct, the constant scan resolves @Filter is not null.
  3. a full text search to resolve CONTAINS(Name, @Filter)
  4. unions the result of 3 with the empty set from 2
  5. loop joins the result of 1 and 4 to look up the other columns

A hash join trades memory for speed; if your system has enough memory, it's much faster than a loop join. This can easily explan a 10-100x slowdown.

One fix is to use two distinct queries:

if @Filter is null
    SELECT TOP 100 ID FROM dbo.Products
else
    SELECT TOP 100 ID FROM dbo.Products WHERE CONTAINS(Name, @Filter)
孤星 2025-01-05 06:13:58

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

You've introduced an OR condition.
In most cases it is simply much faster to check explicitly for NULL and perform one query vs your method.

For instance try this:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文