Hoo*_*igi 8 sql-server full-text-search sql-server-2008-r2
以下查询需要大约 10 秒才能在具有 12k 条记录的表上完成
select top (5) *
from "Physician"
where "id" = 1 or contains("lastName", '"a*"')
Run Code Online (Sandbox Code Playgroud)
但是,如果我将 where 子句更改为
where "id" = 1
Run Code Online (Sandbox Code Playgroud)
或者
where contains("lastName", '"a*"')
Run Code Online (Sandbox Code Playgroud)
它会立即返回。
两列都被索引,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;
Run Code Online (Sandbox Code Playgroud)
这是执行计划
可能是什么问题呢?
Ran*_*gen 12
查看查询计划时,我们可以看到触及一个索引来服务两个过滤操作。
非常简单地说,由于 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);
Run Code Online (Sandbox Code Playgroud)
运行查询
SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 1 OR contains("lastName", '"B*"');
Run Code Online (Sandbox Code Playgroud)
结果与您的查询计划相当:
在上面的例子中,B 不存在于全文索引中。因此,它取决于参数和数据查询计划的效率。
对此的更好解释可以在Paul White 的Row Goals, Part 2: Semi Joins 中找到
...换句话说,在应用程序的每次迭代中,只要找到第一个匹配项,我们就可以停止查看输入 B,使用下推连接谓词。这正是行目标的优点:生成优化计划的一部分以快速返回前 n 个匹配行(此处 n = 1)。
例如,更改谓词以便更快地找到结果(在扫描开始时)。
select top (5) *
from dbo.Person
where "id" = 124
or contains("lastName", '"A*"');
Run Code Online (Sandbox Code Playgroud)
where "id" = 124由于全文索引谓词已经返回 5 行,满足TOP()谓词,因此被消除。
结果也显示了这一点
id lastname
1 'AAA...'
2 'AAA...'
3 'AAA...'
4 'AAA...'
5 'AAA...'
Run Code Online (Sandbox Code Playgroud)
和 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);
Run Code Online (Sandbox Code Playgroud)
运行查询以查找这些先前插入的行
SELECT TOP (2) *
from dbo.Person
where "id" = 1
or contains("lastName", '"B*"');
Run Code Online (Sandbox Code Playgroud)
这再次导致几乎所有行的迭代次数过多,无法返回找到的最后一个值。
id lastname
1 'AAA...'
12001 'BBB...'
Run Code Online (Sandbox Code Playgroud)
使用跟踪标志 4138 删除行目标时
SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 124
OR contains("lastName", '"B*"')
OPTION(QUERYTRACEON 4138 );
Run Code Online (Sandbox Code Playgroud)
优化器使用更接近于实现 a 的连接模式UNION,在我们的例子中这是有利的,因为它将谓词下推到它们各自的聚集索引查找,并且不使用行目标左半连接运算符。
另一种写法,不使用上面提到的跟踪标志:
SELECT top (5) *
FROM
(
SELECT *
FROM dbo.Person
WHERE "id" = 1
UNION
SELECT *
FROM dbo.Person
WHERE contains("lastName", '"B*"')
) as A;
Run Code Online (Sandbox Code Playgroud)
使用结果查询计划:
直接应用全文功能的地方
作为旁注,对于 op,查询优化器修补程序跟踪标志 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*"')
Run Code Online (Sandbox Code Playgroud)
在第一次执行时,急切假脱机在返回 Predicate 请求的行子集之前读取并存储整个输入 后来执行从工作表中读取并返回相同或不同的行子集,而不必执行子行再次节点。
将搜索谓词应用于此索引热切假脱机:
| 归档时间: |
|
| 查看次数: |
562 次 |
| 最近记录: |