对于 CONTAINS 全文谓词,SQL Server 2008 中的逻辑短路似乎失败

Posted

技术标签:

【中文标题】对于 CONTAINS 全文谓词,SQL Server 2008 中的逻辑短路似乎失败【英文标题】:Logical short circuiting in SQL Server 2008 seems to fail for the CONTAINS full text predicate 【发布时间】:2012-09-06 07:49:15 【问题描述】:

我有一个非常复杂的查询,有时返回结果非常缓慢。从一开始就很清楚,罪魁祸首是与全文搜索有关的 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 语句也会运行(但不一定包含在结果中,这是正确的),执行时间从 1 秒以下到 6 秒秒或更多,这取决于有效语句的高度,因此后面还有多少其他 CONTAINS 谓词。由此得出的明显结论是,由于某种原因,以下布尔语句无法短路并运行 CONTAINS 谓词。

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

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

【问题讨论】:

no 保证将评估哪些谓词以及何时评估,即使使用 CASE(我曾经相信 CASE 可以用于强制排序,但是事实证明并非如此) 由于这似乎与搜索相关,您可能想阅读 Erland Sommarskog 的优秀 Dynamic Search conditions in SQL 好吧,据我测试,'CASE' 无论如何都不能与 CONTAINS 一起使用,但感谢您的注意。 也感谢您的链接。我刚读完它,对我来说有几个新的有用点。主要是它说服了我一个明显的事实,即动态 SQL 并不像我很久以前所认为的那样“邪恶”。除非有人可以提供更好的建议,否则我可能需要使用这些 procs。我只是不想这样做,因为这些选择有 400 行长,因此在更新它们时获得颜色编码是很好的。 【参考方案1】:

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

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

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

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

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

【讨论】:

以上是关于对于 CONTAINS 全文谓词,SQL Server 2008 中的逻辑短路似乎失败的主要内容,如果未能解决你的问题,请参考以下文章

7645 Null 或空全文谓词

不能在表或索引视图上使用 CONTAINS 或 FREETEXT 谓词,因为它不是全文索引

CONTAINS 谓词在 SQL Server 2008 中也不返回它应该返回的所有行

SQL contains查询语句

在sql server中参数化全文查询

SQL Server 全文搜索 CONTAINS() 是不是容易受到 SQL 注入的攻击?