在JOIN中使用时,使用Containstable的Sql服务全文搜索非常慢!

Bob*_*Bob 12 sql-server performance full-text-search

我正在使用sql 2008全文搜索,我遇到严重的性能问题,具体取决于我如何使用Contains或ContainsTable.

下面是示例:(表一有大约5000条记录,table1上有一个覆盖索引,其中包含where子句中的所有字段.我试图简化语句,如果有语法问题,请原谅我.)

场景1:

select * from table1 as t1
where t1.field1=90
and   t1.field2='something'
and   Exists(select top 1 * from containstable(table1,*, 'something') as t2 
where t2.[key]=t1.id)
Run Code Online (Sandbox Code Playgroud)

结果:10秒(非常慢)

场景2:

select * from table1 as t1
join containstable(table1,*, 'something') as t2 on t2.[key] = t1.id
where t1.field1=90
and   t1.field2='something'
Run Code Online (Sandbox Code Playgroud)

结果:10秒(非常慢)

场景3:

Declare @tbl Table(id uniqueidentifier primary key)
insert into @tbl select {key] from containstable(table1,*, 'something')

select * from table1 as t1
where t1.field1=90
and   t1.field2='something'
and  Exists(select id from @tbl as tbl where id=req1.id)
Run Code Online (Sandbox Code Playgroud)

结果:一秒的分数(超快)

最重要的是,似乎如果我在任何类型的连接中使用Containstable或者还有其他条件的select语句的where子句条件,那么性能真的很糟糕.此外,如果您查看分析器,数据库中的读取次数将进入顶层.但是,如果我首先进行全文搜索并将结果放入表变量并使用该变量,那么一切都会超快.读数也低得多.它似乎在"糟糕"的情况下,不知何故它陷入一个循环,导致它从数据库中读取多次,但当然我不明白为什么.

现在的问题首先是为什么会发生这种情况?问题二是表变量如何可扩展?如果它产生了成千上万的记录呢?它仍然会很快.

有任何想法吗?谢谢

Bob*_*Bob 11

我在这个问题上花了很长时间,基于运行很多场景,这就是我想到的:

如果在查询中的任何位置包含Contains或ContainsTable,那么这是首先执行而非独立执行的部分.这意味着,即使其余条件将您的搜索限制为仅一个记录,也不包含也不包含对此的关注.所以这就像一个并行执行.

现在,由于全文搜索仅返回Key字段,因此它会立即查找Key作为为查询选择的其他索引的第一个字段.因此,对于上面的示例,它使用[key],field1,field2查找索引.问题是它根据where子句中的字段为其余查询选择索引.所以对于上面的例子,它选择我所拥有的覆盖索引,类似于field1,field2,Id.(表的Id与全文搜索返回的[Key]相同).总结如下:

  1. 执行包含
  2. 执行查询的其余部分并根据查询的where子句选择索引
  3. 它试图合并这两个.因此,如果为查询的其余部分选择的索引以[key]字段开头,则可以.但是,如果索引没有[key]字段作为第一个键,它将开始执行循环.它甚至不进行表扫描,否则通过5000条记录就不会那么慢.它执行循环的方式是它运行循环,以获取来自FTS的结果总数乘以查询其余部分的结果总数.因此,如果FTS返回2000条记录并且查询的其余部分返回3000,则循环2000*3000 = 6,000,000.我不懂为什么.

所以在我的情况下,它进行全文搜索,然后它完成了查询的其余部分,但选择了我所拥有的覆盖索引,它基于field1,field2,id(这是错误的),结果它搞砸了.如果我将覆盖的索引更改为Id,field1,field2,一切都会非常快.

我的期望是FTS返回一堆[key],其余的查询返回一堆[Id],然后Id应与[key]匹配.

当然,我试图在这里简化我的查询,但实际的查询要复杂得多,我不能只改变索引.我也有确实以全文传递的文本为空白的场景,在这些场景中我甚至不想加入containsstable.在那些情况下,将我的覆盖索引更改为将id字段作为第一个字段,将产生灾难.

无论如何,现在我选择了临时表解决方案,因为它对我有用.我也将结果限制为几千,这有助于在记录数量过高时表变量的潜在性能问题.

谢谢


Ghe*_*hen 7

通常它的工作速度非常快:

select t1.*, t2.Rank
    from containstable(table1, field2, 'something') as t2
        join table1 as t1 ON t1.id = t2.Key AND t1.field1=90
    order by t2.Rank desc
Run Code Online (Sandbox Code Playgroud)

放置搜索条件有很大不同:在JOIN或WHERE中.


Mar*_*ith 4

我将在这里猜测您的问题与我链接到的其他线程上的问题相同。您是否发现多个单词搜索词出现问题?

\n\n

如果是这样,我在该线程中的回答将适用。

\n\n

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

\n\n
\n

最重要的是为全文查询选择正确的联接类型。FulltextMatch STVF 的基数\n 估计对于正确的计划非常重要。\n 因此,首先要检查\n FulltextMatch 基数估计。\n 这是索引中\n 的估计命中数全文搜索\n 字符串。例如,在图 3 中的查询中,这应该接近包含术语 \xe2\x80\x98word\xe2\x80\x99 的文档数。在大多数情况下,它应该非常准确,但如果估计值相差很大,您可能会生成错误的计划。对单个术语的估计通常非常好,但估计多个术语(例如短语或 AND 查询)则更为复杂,因为不可能知道索引中术语的交集是什么。将基于索引中术语的频率。\n 如果基数估计良好,则糟糕的计划可能是由查询优化器成本模型引起的。解决计划问题的唯一方法是使用查询提示强制某种类型的联接或 OPTIMIZE FOR。

\n
\n\n

因此,它根本无法从存储的信息中得知这两个搜索词是否可能是完全独立的,还是经常一起出现。也许您应该有 2 个单独的过程,一个用于单词查询,让优化器执行其操作,另一个用于多词搜索词,您强制执行“足够好”的计划(如果您想做一个,sys.dm_fts_index_keywords 可能会有所帮助)自己粗略估计基数)。

\n\n

如果您遇到单个单词查询的问题,链接文章中的这段文字可能适用。

\n\n
\n

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

\n
\n\n

因此您可能需要使用 RECOMPILE 选项。

\n