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',这要好得多。
似乎优化器只使用短语中的第一个单词来搜索基数。
答案 0 :(得分:1)
我看不到链接的执行计划,网络警察正在阻止,所以这只是一个猜测...
如果在没有TOP
和ORDER 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
使用参数化查询,则可以创建计划指南。
ORM
发送逐字 SSMS
中生成正确的计划并将其另存为XML
sp_create_plan_guide
与OPTION 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)
我有更好的解决方案。
予。让我们首先概述一下提出的解决方案,因为它们也可能在某些情况下使用:
选项(HASH JOIN) - 不好,因为您可能会收到错误“由于此查询中定义的提示,查询处理器无法生成查询计划。重新提交查询而不指定任何提示使用SET FORCEPLAN。“
SELECT TOP 1 * FROM(ORIGINAL_SELECT)ORDER BY ... - 当你需要使用你的分页结果时,这是不好的ORIGINAL_SELECT
sp_create_plan_guide - 不好,因为使用plan_guide你必须为特定的sql语句保存计划,这对动态sql语句不起作用(例如由ORM生成)
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)
我之前遇到过同样的问题。
性能取决于您为全文索引选择的唯一索引。
我的表格有两个独特的列 - ID
和article_number
。
查询:
select top 50 id, article_number, name, ...
from ARTICLE
CONTAINS(*,'"BLACK*" AND "WHITE*"')
ORDER BY ARTICLE_NUMBER
如果全文索引连接到ID
,则根据搜索到的单词,它会很慢。
如果全文索引连接到ARTICLE_NUMBER UNIQUE
索引,那么它总是很快。