在where子句中同时包含“ contains”和“ =”时查询缓慢


8

以下查询大约需要10秒钟才能完成具有12k条记录的表

select top (5) *
from "Physician"
where "id" = 1 or contains("lastName", '"a*"')

但是如果我将where子句更改为

where "id" = 1

要么

where contains("lastName", '"a*"')

它会立即返回。

两列都被索引,lastName列也被全文索引。

CREATE TABLE Physician
(
   id         int identity    NOT NULL,
   firstName  nvarchar(100)   NOT NULL,
   lastName   nvarchar(100)   NOT NULL
);

ALTER TABLE Physician
  ADD CONSTRAINT Physician_PK
  PRIMARY KEY CLUSTERED (id);

CREATE NONCLUSTERED INDEX Physician_IX2
   ON Physician (firstName ASC);

CREATE NONCLUSTERED INDEX Physician_IX3
   ON Physician (lastName ASC);

CREATE FULLTEXT INDEX
    ON "Physician" ("firstName" LANGUAGE 0x0, "lastName" LANGUAGE 0x0)
    KEY INDEX "Physician_PK"
    ON "the_catalog"
    WITH stoplist = off;

这是执行计划

可能是什么问题呢?


我刚加入的表定义
Hooman Valibeigi

Answers:


11

您的执行计划

查看查询计划时,我们可以看到触摸了一个索引即可提供两次过滤操作。

在此处输入图片说明

简而言之,由于使用了TOP运算符,因此设置了行目标。有关行目标的更多信息和前提条件,请参见此处

从同一来源:

行目标策略通常意味着优先于非阻塞导航操作(例如,嵌套循环联接,索引查找和查找),而不是基于阻塞,基于集合的操作(例如排序和散列)。只要客户可以从快速启动和稳定的行流中受益(这可能需要更长的总体执行时间,请参见上面的Rob Farley的帖子),这将很有用。还有更明显和传统的用法,例如一次显示结果。

使用具有行目标集的左半联接将整个表探查到过滤器中,以期尽可能快和高效地返回5行。

这不会发生,导致在.Fulltextmatch TVF上进行了许多迭代。

在此处输入图片说明


重新建立

根据您的计划,我能够在某种程度上重现您的问题:

CREATE TABLE dbo.Person(id int not null,lastname varchar(max));

CREATE UNIQUE INDEX ui_id ON  dbo.Person(id)
CREATE FULLTEXT CATALOG ft AS DEFAULT;  
CREATE FULLTEXT INDEX ON dbo.Person(lastname)   
   KEY INDEX ui_id   
   WITH STOPLIST = SYSTEM;  
GO  

INSERT INTO dbo.Person(id,lastname)
SELECT top(12000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)),
REPLICATE(CAST('A' as nvarchar(max)),80000)+ CAST(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as varchar(10))
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2;
CREATE CLUSTERED INDEX cx_Id on dbo.Person(id);

运行查询

SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 1 OR contains("lastName", '"B*"');

结果与您的查询计划相当:

在此处输入图片说明

在上面的示例中,全文索引中不存在B。结果,它取决于参数和数据查询计划的效率。

这方面的一个更好的解释中可以找到行的目标,第2部分:半加入由保罗·怀特

...换句话说,在应用的每次迭代中,我们都可以使用下推连接谓词在找到第一个匹配项后立即停止查看输入B。这正是行目标的优点:生成计划的一部分,该计划经过优化可快速返回前n个匹配行(此处n = 1)。

例如,更改谓词,以便更快地找到结果(在扫描开始时)。

select top (5) *
from dbo.Person
where "id" = 124 
or contains("lastName", '"A*"');

在此处输入图片说明

where "id" = 124被淘汰,由于全文索引断言已经返回5行,满足TOP()谓语。

结果也表明了这一点

id lastname 
1  'AAA...'   
2  'AAA...'
3  'AAA...'
4  'AAA...'
5  'AAA...'

TVF处决:

在此处输入图片说明

插入一些新行

INSERT INTO dbo.Person
SELECT 12001, REPLICATE(CAST('B' as nvarchar(max)),80000);
INSERT INTO dbo.Person
SELECT 12002, REPLICATE(CAST('B' as nvarchar(max)),80000);

运行查询以查找这些先前插入的行

SELECT TOP (2) *
from dbo.Person
where "id" = 1
or contains("lastName", '"B*"');

这再次导致几乎所有行的迭代次数过多,无法返回找到的最后一个值。

在此处输入图片说明

在此处输入图片说明

id   lastname
1     'AAA...'
12001 'BBB...'

解决

使用traceflag 4138删除行目标时

SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 124 
OR contains("lastName", '"B*"')
OPTION(QUERYTRACEON 4138 );

优化器使用更接近于实现a的联接模式UNION,在我们的情况下,这是有利的,因为它将谓词下推至其各自的聚集索引查找,并且不使用针对行的左半联接运算符。

在此处输入图片说明

不使用上述traceflag的另一种写法:

SELECT top (5) *
FROM
(
SELECT * 
FROM dbo.Person
WHERE "id" = 1 
UNION
SELECT * 
FROM dbo.Person
WHERE contains("lastName", '"B*"')
 ) as A;

生成的查询计划:

在此处输入图片说明

直接应用全文功能的地方

在此处输入图片说明

作为旁注,对于op,查询优化器修补程序traceflag 4199解决了他的问题。他通过添加OPTION(QUERYTRACEON(4199))查询来实现这一点。我无法重现这种行为。此修复程序确实包含一个半联接优化:

跟踪标志:4102功能:SQL 9-如果查询的执行计划包含半联接运算符,则查询性能会降低。通常,当查询包含IN关键字或EXISTS关键字时,将生成半联接运算符。启用标志4102和4118可以克服此问题。

资源


额外

在基于成本的优化过程中,优化器还可以向执行计划中添加索引假脱机,由LogOp_Spool Index on fly Eager (或物理对应物)实施

它针对我的数据集执行此操作,TOP(3)但不针对TOP(2)

SELECT TOP (3) *
from dbo.Physician
where "id" = 1
or contains("lastName", '"B*"')  

在此处输入图片说明

第一次执行时,急切的假脱机将读取并存储整个输入,然后再返回谓词所请求的行子集。以后的执行从工作表中读取并返回行的相同或不同子集,而不必执行子级节点。

资源

将搜寻谓词应用于该索引急切假脱机:

在此处输入图片说明


您能否解释一下跟踪标志的用法?从您的代码中还不清楚他们在做什么
George.Palacios

1
@ George.Palacios是的,我弄得一团糟:^)。感谢您的反馈!
兰迪·沃顿根

您建议的QUERYTRACEON标志(4138、3604、8607、8612)中的任何一个都不起作用,但是QUERYTRACEON 4199恰好可以解决问题!
Hooman Valibeigi

请注意,即使没有TOP运算符,查询也很慢
Hooman Valibeigi

@HoomanValibeigi您是否尝试过底部的联合解决方案?
兰迪·沃顿根
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.