Kế hoạch thực hiện của bạn
Khi nhìn vào kế hoạch truy vấn, chúng ta có thể thấy rằng một chỉ mục được chạm để phục vụ hai hoạt động của bộ lọc.
Rất đơn giản, do toán tử TOP, một mục tiêu hàng đã được đặt. Nhiều thông tin và điều kiện tiên quyết về các mục tiêu hàng có thể được tìm thấy ở đây
Từ cùng một nguồn:
Chiến lược mục tiêu hàng nói chung có nghĩa là ưu tiên các hoạt động điều hướng không chặn (ví dụ: các vòng lặp lồng nhau tham gia, tìm kiếm chỉ mục và tra cứu) trong việc chặn, các hoạt động dựa trên tập hợp như sắp xếp và băm. Điều này có thể hữu ích bất cứ khi nào khách hàng có thể hưởng lợi từ việc khởi động nhanh và dòng hàng ổn định (có lẽ thời gian thực hiện tổng thể dài hơn - xem bài đăng của Rob Farley ở trên). Ngoài ra còn có cách sử dụng rõ ràng và truyền thống hơn, ví dụ như trong việc trình bày kết quả một trang tại một thời điểm.
Toàn bộ bảng được thăm dò vào các bộ lọc với việc sử dụng liên kết bán bên trái có mục tiêu hàng được đặt, hy vọng sẽ trả về 5 hàng nhanh nhất và hiệu quả nhất có thể.
Điều này không xảy ra, dẫn đến nhiều lần lặp lại trên TVF .ulltextmatch.
Tái tạo
Dựa trên kế hoạch của bạn , tôi đã có thể tái tạo phần nào vấn đề của bạn:
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);
Chạy truy vấn
SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 1 OR contains("lastName", '"B*"');
Kết quả thành một kế hoạch truy vấn có thể so sánh với bạn:
Trong ví dụ trên, B không tồn tại trong chỉ mục fulltext. Kết quả là nó phụ thuộc vào tham số & dữ liệu hiệu quả của kế hoạch truy vấn.
Một lời giải thích tốt hơn về điều này có thể được tìm thấy trong Row Goals, Phần 2: Semi Joins của Paul White
... Nói cách khác, trên mỗi lần lặp lại của một ứng dụng, chúng ta có thể ngừng nhìn vào đầu vào B ngay khi tìm thấy kết quả khớp đầu tiên, sử dụng biến vị ngữ nối xuống. Đây chính xác là loại điều mà mục tiêu hàng phù hợp để: tạo ra một phần của kế hoạch được tối ưu hóa để nhanh chóng trả lại n hàng đầu tiên (trong đó n = 1 ở đây).
Ví dụ: thay đổi vị ngữ để kết quả được tìm thấy sớm hơn (khi bắt đầu quét).
select top (5) *
from dbo.Person
where "id" = 124
or contains("lastName", '"A*"');
các where "id" = 124
bị loại bỏ do vị chỉ số toàn văn đã trở về 5 hàng, đáp ứng được các TOP()
vị ngữ.
Kết quả cũng cho thấy điều này
id lastname
1 'AAA...'
2 'AAA...'
3 'AAA...'
4 'AAA...'
5 'AAA...'
Và TVF thực thi:
Chèn một số hàng mới
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);
Chạy truy vấn để tìm các hàng được chèn trước đó
SELECT TOP (2) *
from dbo.Person
where "id" = 1
or contains("lastName", '"B*"');
Điều này một lần nữa dẫn đến quá nhiều lần lặp lại trên hầu hết tất cả các hàng để trả về giá trị cuối cùng nhưng một giá trị được tìm thấy.
id lastname
1 'AAA...'
12001 'BBB...'
Giải quyết
Khi xóa mục tiêu hàng bằng cách sử dụng trackflag 4138
SELECT TOP (5) *
FROM dbo.Person
WHERE "id" = 124
OR contains("lastName", '"B*"')
OPTION(QUERYTRACEON 4138 );
Trình tối ưu hóa sử dụng mô hình nối gần hơn để thực hiện a UNION
, trong trường hợp của chúng tôi, điều này là thuận lợi vì nó đẩy các biến vị ngữ xuống chỉ số cụm được tìm kiếm tương ứng của chúng và không sử dụng toán tử nửa liên kết được nhắm mục tiêu.
Một cách khác để viết điều này, mà không sử dụng dấu vết được đề cập ở trên:
SELECT top (5) *
FROM
(
SELECT *
FROM dbo.Person
WHERE "id" = 1
UNION
SELECT *
FROM dbo.Person
WHERE contains("lastName", '"B*"')
) as A;
Với kế hoạch truy vấn kết quả:
nơi chức năng fulltext được áp dụng trực tiếp
Là một sidenote, đối với op, trình tối ưu hóa truy vấn hotfix trackflag 4199 đã giải quyết vấn đề của anh ta. Ông đã thực hiện điều này bằng cách thêm OPTION(QUERYTRACEON(4199))
vào truy vấn. Tôi đã không thể tái tạo hành vi đó vào cuối của tôi. Hotfix này có chứa tối ưu hóa bán tham gia:
Cờ theo dõi: 4102 Chức năng: SQL 9 - Hiệu suất truy vấn chậm nếu kế hoạch thực hiện truy vấn có chứa toán tử bán tham gia Thông thường, toán tử bán tham gia được tạo khi truy vấn chứa từ khóa IN hoặc từ khóa EXISTS. Cho phép cờ 4102 và 4118 để khắc phục điều này.
Nguồn
Thêm
Trong quá trình tối ưu hóa dựa trên chi phí, trình tối ưu hóa cũng có thể thêm một bộ đệm chỉ mục vào kế hoạch thực hiện, được thực hiện bởi LogOp_Spool Index on fly Eager
(hoặc đối tác vật lý)
Nó thực hiện điều này với tập dữ liệu của tôi cho TOP(3)
nhưng không phải choTOP(2)
SELECT TOP (3) *
from dbo.Physician
where "id" = 1
or contains("lastName", '"B*"')
Trong lần thực hiện đầu tiên, một bộ đệm háo hức đọc và lưu trữ toàn bộ đầu vào trước khi trả về tập hợp con của các hàng được yêu cầu bởi các thực thi Dự đoán sau đó đọc và trả về cùng một hoặc một tập hợp con khác của các hàng từ bàn làm việc, mà không bao giờ phải thực thi con nút một lần nữa.
Nguồn
Với vị từ tìm kiếm được áp dụng cho chỉ số háo hức này: