4

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。为什么会这样?怎样才能做得更快?

2010 年 5 月 3 日更新

看起来基数在多词 FreeText 搜索中不正常:

优化器估计有 28K 记录匹配“White Dress”,而实际上只有 1 个。 http://screencast.com/t/NjM3ZjE4NjAt

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

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

4

6 回答 6

3

看起来 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_guide与 an一起使用OPTION USE PLAN以强制优化器始终使用此计划。
于 2010-04-30T17:52:09.737 回答
1

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

如果它在没有TOPand的情况下运行得很快,请ORDER BY尝试这样做:

SELECT TOP 1
    *
    FROM (SELECT 
              ID, Name, DateMadeNew 
              FROM Product 
              WHERE contains(Name, '"White Dress"')
         ) dt
    ORDER BY DateMadeNew desc
于 2010-04-30T17:21:29.507 回答
1

对此有几点想法:

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

2) 您使用的是什么版本的 SQL Server?我在 SQL Server 2008 中遇到了类似的问题,结果证明只是没有安装 Service Pack 1。安装 SP1 和一个需要几分钟的 FreeText 查询(由于实际执行的数量巨大)下降到需要一秒钟。

于 2010-04-30T20:46:23.780 回答
1

编辑

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

最重要的是为全文查询选择了正确的连接类型。FulltextMatch STVF 的基数估计对于正确的计划非常重要。所以首先要检查的是 FulltextMatch 基数估计。这是全文搜索字符串的索引中的估计命中数。例如,在图 3 中的查询中,这应该接近于包含术语“单词”的文档数量。在大多数情况下,它应该是非常准确的,但如果估计相差很远,您可能会产生糟糕的计划。单项的估计通常非常好,但是估计多个术语(例如短语或 AND 查询)更加复杂,因为无法根据索引中术语的频率知道索引中术语的交集是什么。如果基数估计良好,则查询优化器成本模型可能会导致计划不佳。解决计划问题的唯一方法是使用查询提示来强制执行某种连接或 OPTIMIZE FOR。

因此,它根本无法从它存储的信息中知道这两个搜索词是否可能非常独立或共同出现。也许您应该有 2 个单独的过程,一个用于单字查询,让优化器对其进行处理,另一个用于强制执行“足够好”计划的多字过程(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
于 2010-05-01T08:36:16.120 回答
1

我有更好的解决方案。

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

  1. OPTION (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 生成)

二、我的解决方案包含两部分 1. 用于全文搜索的自连接表 2. 使用 MS SQL HASH 连接提示MSDN 连接提示

你的 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,我已经在帖子中回复了如何编写拦截器来修改您的查询以将 INNER JOIN 替换为 INNER HASH JOIN

于 2010-12-30T09:13:52.513 回答
1

我之前也有同样的问题。

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

查询:

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

如果连接到全文索引,ID则速度会很慢,具体取决于搜索的单词。如果全文索引连接到ARTICLE_NUMBER UNIQUE索引,那么它总是很快。

于 2012-09-25T09:31:32.280 回答