2012-09-13 99 views
2

我有一个非常复杂的查询,它有时返回的结果非常缓慢。从一开始就很明显,罪魁祸首是与全文搜索有关的WHERE子句的一部分。所以我将其隔离并进行了测试。这个测试结果发现它什么时候发生,但我至今未能弄清楚如何修复它。这里的问题是什么:SQL Server 2008中的逻辑短路似乎失败CONTAINS全文谓词

我需要基于用户想要做一个,两个或三个全文索引列进行筛选。因为它似乎是内部的列列表中包含谓词不能suplied可变我能想出是退而求其次采用一些标准的布尔逻辑,像这样:

(((@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谓词遵循。由此得出的明显结论是,由于某些原因,无论如何,以下布尔语句无法短路并运行CONTAINS谓词。

据我了解,执行顺序不是由实际的代码和SQL优化后的一些短路可能无法按设计全靠它,这似乎是这种情况下的工作一直给予。那里的建议通常是使用CASE语句来确保顺序,但不幸的是,CONTAINS语句在CASE语句中似乎并不满意。

所以,我想我大概那种知道为什么它正在发生(的SQL优化),但不能想出办法如何真正解决这个问题。谁能帮忙?

+2

有* *无*保证哪些谓词将被评估,何时,即使使用'CASE'(我曾经认为'CASE'可以用来强加排序,但事实证明并非如此) –

+1

既然这似乎与搜索有关,您可能想要阅读Erland Sommarskog出色的[SQL中的动态搜索条件](http://www.sommarskog.se/dyn-search.html) –

+0

好吧,据我测试'CASE'无论如何都不能和CONTAINS一起使用,但是谢谢你的提示。 – Marek

回答

1

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

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

这不是一个可怕的解决方案,因为全文索引反正产生局部重新编译(你可以根据传递的是函数查询得到不同的计划)。

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

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