FreeText查询速度很慢 - 包括TOP和Order By

时间:2010-04-30 17:05:30

标签: sql sql-server full-text-search freetext

Product表中有700K记录。查询:

SELECT TOP 1 ID, Name FROM Product WHERE contains(Name, '"White Dress"') ORDER BY DateMadeNew desc

需要大约1分钟才能运行。 DateMadeNew上有一个非聚集索引,Name上有一个FreeText索引。

如果我删除TOP 1或Order By - 运行时间不到1秒。

这是执行计划的链接。 http://screencast.com/t/ZDczMzg5N

看起来FullTextMatch的执行次数超过400K。为什么会这样?怎么能更快?

更新5/3/2010

在多字FreeText搜索中看起来基数没有重击:

优化器估计有28K记录与“白色连衣裙”相匹配,而实际上只有1个。 http://screencast.com/t/NjM3ZjE4NjAt

如果我将'White Dress'替换为'White',估计数字是'27,951',而实际数字是'28,487',这要好得多。

似乎优化器只使用短语中的第一个单词来搜索基数。

6 个答案:

答案 0 :(得分:1)

我看不到链接的执行计划,网络警察正在阻止,所以这只是一个猜测...

如果在没有TOPORDER BY的情况下快速运行,请尝试这样做:

SELECT TOP 1
    *
    FROM (SELECT 
              ID, Name, DateMadeNew 
              FROM Product 
              WHERE contains(Name, '"White Dress"')
         ) dt
    ORDER BY DateMadeNew desc

答案 1 :(得分:1)

  

看起来FullTextMatch的执行次数超过400K。为什么会这样?

由于您的索引与TOP 1结合使用,优化程序认为最好遍历索引,检查条目的每条记录。

  

怎样才能更快?

如果更新统计信息无效,请尝试在查询中添加提示:

SELECT  TOP 1 *
FROM    product pt
WHERE   CONTAINS(name, '"test1"')
ORDER BY
        datemadenew DESC
OPTION (HASH JOIN)

这将强制引擎使用HASH JOIN算法来连接您的表和全文查询的输出。

全文查询被视为远程源,返回由KEY INDEX定义中提供的FULLTEXT INDEX索引的值集。

<强>更新

如果您的ORM使用参数化查询,则可以创建计划指南。

  • 使用Profiler拦截ORM发送逐字
  • 的查询
  • 使用提示在SSMS中生成正确的计划并将其另存为XML
  • sp_create_plan_guideOPTION USE PLAN一起使用以强制优化程序始终使用此计划。

答案 2 :(得分:1)

修改

来自http://technet.microsoft.com/en-us/library/cc721269.aspx#_Toc202506240

  

最重要的是   选择了正确的连接类型   全文查询。基数   对FulltextMatch STVF的估计   对于正确的计划非常重要。   所以首先要检查的是   FulltextMatch基数估计。   这是估计的点击次数   在全文搜索的索引中   串。例如,在查询中   图3这应该接近了   包含的文件数量   术语'字'。在大多数情况下它应该   非常准确,但如果估计   很久以前,你可以   产生糟糕的计划。估计   单一术语通常非常好,   但估计多个术语,如   短语或AND查询更复杂   因为不可能知道什么   索引中的术语交集   将根据频率而定   索引中的术语。如果基数   估计是好的,一个糟糕的计划   可能是由查询引起的   优化器成本模型。唯一的方法   修复计划问题是使用查询   提示强制某种加入   或OPTIMIZE FOR。

因此,根据它存储的信息,它根本无法知道这两个搜索项是否可能是完全独立的,或者通常是一起找到的。也许你应该有两个独立的程序,一个用于单词查询,你让优化器执行它的东西,一个用于多字程序,你强制一个“足够好”的计划(sys.dm_fts_index_keywords可能有帮助,如果你不想要一个一个尺寸适合所有计划)。

注意:您的单字程序可能需要WITH RECOMPILE选项查看文章的这一部分。

  

在SQL Server 2008全文搜索中,我们可以根据所使用的搜索词的基数估算来更改生成的计划。如果查询计划是固定的(因为它位于存储过程内的参数化查询中),则不会执行此步骤。因此,即使此计划不适合给定的搜索词,编译后的计划也始终提供此查询。

原始答案

你的新计划看起来仍然很糟糕。看起来它只从全文查询部分返回1行,但扫描Product表中的所有770159行。

这是如何表现的?

CREATE TABLE #tempResults
(
ID int primary key,
Name varchar(200),
DateMadeNew datetime
)

INSERT INTO #tempResults
SELECT 
      ID, Name, DateMadeNew 
      FROM Product 
      WHERE contains(Name, '"White Dress"')


SELECT TOP 1
    *
    FROM #tempResults
    ORDER BY DateMadeNew desc

答案 3 :(得分:1)

我有更好的解决方案。

予。让我们首先概述一下提出的解决方案,因为它们也可能在某些情况下使用:

  1. 选项(HASH JOIN) - 不好,因为您可能会收到错误“由于此查询中定义的提示,查询处理器无法生成查询计划。重新提交查询而不指定任何提示使用SET FORCEPLAN。“

  2. SELECT TOP 1 * FROM(ORIGINAL_SELECT)ORDER BY ... - 当你需要使用你的分页结果时,这是不好的ORIGINAL_SELECT

  3. sp_create_plan_guide - 不好,因为使用plan_guide你必须为特定的sql语句保存计划,这对动态sql语句不起作用(例如由ORM生成)

  4. II。我的解决方案包含两部分     1.用于全文搜索的自联接表     2.使用MS SQL HASH加入提示MSDN Join Hints

    你的SQL:

    SELECT TOP 1 ID, Name FROM Product WHERE contains(Name, '"White Dress"') 
    ORDER BY DateMadeNew desc
    

    应改写为:

    SELECT TOP 1 p.ID, p.Name FROM Product p INNER HASH JOIN Product fts ON fts.ID = p.ID
    WHERE contains(fts.Name, '"White Dress"') 
    ORDER BY p.DateMadeNew desc
    

    如果您使用带有/不带Castle Active Records的NHibernate,我在post中回复了如何编写拦截器以修改您的查询以通过INNER HASH JOIN替换INNER JOIN

答案 4 :(得分:0)

关于这一点的几点想法:

1)您是否更新了Product表的统计信息?在那里查看操作的估计值和实际行数也很有用。

2)您使用的是哪个版本的SQL Server?我遇到过与SQL Server 2008类似的问题,结果只是没有安装Service Pack 1。安装SP1和一个花费几分钟的FreeText查询(由于实际执行的大量实际执行)导致花费一秒钟。

答案 5 :(得分:0)

我之前遇到过同样的问题。

性能取决于您为全文索引选择的唯一索引。 我的表格有两个独特的列 - IDarticle_number

查询:

select top 50 id, article_number, name, ... 
from ARTICLE 
CONTAINS(*,'"BLACK*" AND "WHITE*"')
ORDER BY ARTICLE_NUMBER

如果全文索引连接到ID,则根据搜索到的单词,它会很慢。 如果全文索引连接到ARTICLE_NUMBER UNIQUE索引,那么它总是很快。