2

我有一个非常复杂的查询,它有时会非常缓慢地返回结果。从一开始就很清楚,罪魁祸首是与全文搜索有关的 WHERE 子句的一部分。所以我将其隔离并进行了测试。该测试导致确定它何时发生,但到目前为止我未能弄清楚如何修复它。这是问题所在:

我需要根据用户想要执行的操作筛选一个、两个或三个全文索引列。因为似乎 CONTAINS 谓词中的列列表不能作为变量提供,所以我能想到的下一个最好的办法是使用一些标准的布尔逻辑,如下所示:

(((@SearchInName = 0 AND @SearchInShortDescr = 0 AND @SearchInHTMLDescr = 0) OR @SearchExpression = '""')
OR (@SearchInName = 1 AND @SearchInShortDescr = 0 AND @SearchInHTMLDescr = 0 AND CONTAINS(ProductName, @SearchExpression))
OR (@SearchInName = 0 AND @SearchInShortDescr = 1 AND @SearchInHTMLDescr = 0 AND CONTAINS(ProductShortDescr, @SearchExpression))
OR (@SearchInName = 0 AND @SearchInShortDescr = 0 AND @SearchInHTMLDescr = 1 AND CONTAINS(ProductDescrHTML, @SearchExpression))
OR (@SearchInName = 1 AND @SearchInShortDescr = 1 AND @SearchInHTMLDescr = 0 AND CONTAINS((ProductName, ProductShortDescr), @SearchExpression))
OR (@SearchInName = 1 AND @SearchInShortDescr = 0 AND @SearchInHTMLDescr = 1 AND CONTAINS((ProductName, ProductDescrHTML), @SearchExpression))
OR (@SearchInName = 0 AND @SearchInShortDescr = 1 AND @SearchInHTMLDescr = 1 AND CONTAINS((ProductShortDescr, ProductDescrHTML), @SearchExpression))
OR (@SearchInName = 1 AND @SearchInShortDescr = 1 AND @SearchInHTMLDescr = 1 AND CONTAINS((ProductName, ProductShortDescr, ProductDescrHTML), @SearchExpression))

只要实际的有效条件与最后一个 OR 部分匹配,那就可以了,并且可以按预期(快速)工作。所以在上面的例子中,当@SearchInName = 1 AND @SearchInShortDescr = 1 AND @SearchInHTMLDescr = 1 时(用户想要搜索所有 3 列)。如果将其他条件放在此代码块的最后,其他条件也会快速返回。但是,一旦实际有效条件高于上一个,似乎它后面的所有 CONTAINS 语句也会运行(尽管不一定包含在结果中,这是正确的),这将执行时间从 1s 到 6秒或更多,这取决于有效语句的高度,因此后面还有多少其他 CONTAINS 谓词。

我知道执行顺序并不总是由实际代码给出,并且在 SQL 优化之后,由于它的原因,一些短路可能无法按设计工作,这似乎就是这种情况。通常的建议是使用 CASE 语句来确保顺序,但不幸的是,CONTAINS 语句在 CASE 语句中似乎并不令人满意。

所以我想我可能有点知道它为什么会发生(SQL 优化),但无法弄清楚如何实际解决这个问题。任何人都可以帮忙吗?

4

1 回答 1

1

Sql server 将缓存该计划,以便可以重复使用。直接的含义是 SQL 服务器创建的计划必须适用于所有情况,因此必须是通用的。这消除了任何短路逻辑的可能性。

一种解决方案是使用 OPTION (RECOMPILE) 查询提示。这将强制 SQL 服务器在每次执行时重新编译查询。因为 SQL 知道该计划永远不会被重用,它会检查参数的值,应用短路逻辑,选择过滤索引,并制定可能的最佳计划。

这不是一个糟糕的解决方案,因为全文索引无论如何都会生成部分重新编译(您可以根据传递给 CONTAINS 函数的查询获得不同的计划)。

另一种方法是使用动态 SQL 来构建查询。使用动态 SQL,您将获得两全其美:更好的计划和计划重用。

你使用哪种技术重要吗?在大多数情况下,不,两者都可以。如果您的查询被非常频繁地调用,例如,每天 3-4 百万次调用,那么每秒编译次数可能会成为瓶颈。如果发生这种情况,动态 SQL 方法是有利的。

于 2013-04-25T03:30:39.927 回答