2013-05-08 71 views
9

我对上的SQL Server 2008R2一个简单的表自由文本目录:结合自由文本搜索与另一个条件是

CREATE FULLTEXT CATALOG customer_catalog; 
CREATE FULLTEXT INDEX ON customer 
( 
    name1 
) 
    KEY INDEX customer_pk 
    ON customer_catalog; 
ALTER FULLTEXT INDEX ON customer START UPDATE POPULATION; 

如果我几乎立即执行以下三个查询的前两回,而最后一个需要14秒左右在桌子上有100,000个记录:

SELECT 
     customer_id 
    FROM 
     customer 
    WHERE 
     CONTAINS(customer.*, 'nomatch'); 

SELECT 
     customer_id 
    FROM 
     customer 
    WHERE 
     customer.customer_id = 0; 

SELECT 
     customer_id 
    FROM 
     customer 
    WHERE 
     CONTAINS(customer.*, 'nomatch') 
      OR customer.customer_id = 0; 

这里是queryplans:

enter image description here

为什么第三个查询慢得多?我可以做任何事情来改善它,或者我需要拆分查询吗?

+0

通常,将'CONTAINS'查询重写为'CONTAINSTABLE'会将此类问题排序。 [例如在这个答案](http://stackoverflow.com/questions/2906812/adding-more-or-searches-with-contains-brings-query-to-crawl/2907331#2907331) – 2013-05-11 22:09:58

+1

根据你的2008R2 SP版本,您的问题可能与下面的MS Connect问题有关:http://connect.microsoft.com/SQLServer/feedback/details/520653/full-text-performance-with-mixed-queries – MicSim 2013-05-17 14:16:13

+0

@MicSim:如果您使成为答案,我会接受它。虽然其他答案提供了很好的解决方法,但您的看起来像是真正的答案。谢谢! – 2013-05-17 17:36:21

回答

2

根据您的MS SQL 2008 R2服务包版本,您的问题可能与以下Microsoft Connect问题有关:Full-text performance with "mixed queries"

根据MS Connect条目,在为SQL Server 2008 R2安装最新的累积更新包后,问题应该消失。

3

很难说为什么,但似乎SQL Server正在选择一个效率低下的查询计划。下面是一些建议:

更新表上的统计信息:

UPDATE STATISTICS dbo.customer 

一旦统计数据是最新的,你可以再次尝试你的查询,看看是否有改善。

还有一点是,对于组合的OR语句,SQL Server正在使用索引扫描,而不是查找。你可以尝试FORCESEEK提示,看看是否有差别:其他

SELECT customer_id 
FROM customer WITH (FORCESEEK) 
WHERE CONTAINS(customer.*, 'nomatch') 
OR customer.customer_id = 0; 

一种选择,正如你所说,是分裂的语句。下面UNION执行的一样好,你的前两个语句组合:

SELECT customer_id FROM customer 
WHERE CONTAINS(customer.*, 'nomatch') 

UNION 

SELECT customer_id FROM customer 
WHERE customer.customer_id = 0 

更新 - 上面的查询变为UNION而不是UNION ALL

由于@PondLife在评论中指出,我的意思是在上述查询中执行UNION而不是UNION ALL。想过之后,我也尝试了UNION ALL,它似乎更快。这是假设你不关心重复的ID:

SELECT customer_id FROM customer 
WHERE CONTAINS(customer.*, 'nomatch') 

UNION ALL 

SELECT customer_id FROM customer 
WHERE customer.customer_id = 0 
+0

我认为你的意思是'UNION',而不是'UNION ALL'(在这个特定情况下),否则包含'nomatch'*和*的ID 0的行将在结果集中出现两次而不是一次。 – Pondlife 2013-05-10 20:43:49

+0

@Pondlife - 你是对的,我会更新。谢谢。 – 2013-05-10 20:46:54

3

的“OR” logicial条件往往使查询的运行速度很慢:/ 通常情况下,最好的选择是使用UNION(ALL)。

在你的情况,我很好奇你做的

SELECT 
    customer_id 
FROM 
    customer 
WHERE 
    customer.customer_id = 0; 

它只会导致零的列表(可能为空)的使用。 计数(!)有多少客户有一个id = 0? 是否要检查是否有客户的ID为0?

如果不是计数为零,但要知道,如果他们有,那么这个查询应该是有效的:

SELECT 
    customer_id 
FROM 
    customer 
WHERE 
    CONTAINS(customer.*, 'nomatch') 
    AND customer.customer_id <> 0 
UNION ALL 
SELECT TOP(1) 
    0 
FROM 
    customer 
WHERE 
    customer.customer_id = 0 

否则高效的查询是这样的一个:

SELECT 
    customer_id 
FROM 
    customer 
WHERE 
    CONTAINS(customer.*, 'nomatch') 
    AND customer.customer_id <> 0 
UNION ALL 
SELECT 
    0 
FROM 
    customer 
WHERE 
    customer.customer_id = 0 

(我刚刚删除了TOP子句)

+0

'customer_id = 0'只是为了展示一个简单的例子。实际的查询是两个包含CONTAINS(table1。*)或CONTAINS(table2。*)的表的连接。但是我意识到只要在id列上查询就可以重现问题,所以我认为这将是一个更简单的例子。 – 2013-05-13 10:41:27

+0

条件不重要,使用我的第二个查询。从使用全文索引的部分开始,并对其进行过滤以排除第二部分(在本例中使用customer.customer_id <> 0),然后在第二部分创建一个全部联合。 – Serge 2013-05-15 07:57:58

相关问题