Đây là một quyết định của tối ưu hóa dựa trên chi phí.
Các chi phí ước tính được sử dụng trong lựa chọn này là không chính xác vì nó giả định tính độc lập thống kê giữa các giá trị trong các cột khác nhau.
Nó tương tự như vấn đề được mô tả trong Row Goals Gone Rogue trong đó số chẵn và số lẻ có mối tương quan nghịch.
Nó rất dễ dàng để sinh sản.
CREATE TABLE dbo.animal(
id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
colour varchar(50) NOT NULL,
species varchar(50) NOT NULL,
Filler char(10) NULL
);
/*Insert 20 million rows with 1% black and 1% swan but no black swans*/
WITH T
AS (SELECT TOP 20000000 ROW_NUMBER() OVER (ORDER BY @@SPID) AS RN
FROM master..spt_values v1,
master..spt_values v2,
master..spt_values v3)
INSERT INTO dbo.animal
(colour,
species)
SELECT CASE
WHEN RN % 100 = 1 THEN 'black'
ELSE CAST(RN % 100 AS VARCHAR(3))
END,
CASE
WHEN RN % 100 = 2 THEN 'swan'
ELSE CAST(RN % 100 AS VARCHAR(3))
END
FROM T
/*Create some indexes*/
CREATE NONCLUSTERED INDEX ix_species ON dbo.animal(species);
CREATE NONCLUSTERED INDEX ix_colour ON dbo.animal(colour);
Bây giờ cố gắng
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black'
AND species LIKE 'swan'
Điều này đưa ra kế hoạch dưới đây được chi phí tại 0.0563167
.
Kế hoạch có thể thực hiện liên kết hợp nhất giữa kết quả của hai chỉ mục trên id
cột. ( Thêm chi tiết về thuật toán hợp nhất ở đây ).
Hợp nhất tham gia yêu cầu cả hai đầu vào được sắp xếp theo khóa tham gia.
Các chỉ mục không bao gồm được sắp xếp theo thứ tự (species, id)
và (colour, id)
tương ứng (các chỉ mục không phân cụm không luôn luôn có bộ định vị hàng được thêm vào cuối khóa nếu không được thêm một cách rõ ràng). Truy vấn không có bất kỳ ký tự đại diện nào đang thực hiện tìm kiếm đẳng thức vào species = 'swan'
và colour ='black'
. Vì mỗi tìm kiếm chỉ lấy một giá trị chính xác từ cột hàng đầu, các hàng khớp sẽ được sắp xếp theo thứ tự do id
đó kế hoạch này là có thể.
Truy vấn các nhà khai thác kế hoạch thực hiện từ trái sang phải . Với toán tử bên trái yêu cầu các hàng từ con của nó, lần lượt yêu cầu các hàng từ con của chúng (và cứ thế cho đến khi đạt được các nút lá). Trình TOP
lặp sẽ dừng yêu cầu thêm bất kỳ hàng nào từ con của nó sau khi nhận được 10 hàng.
SQL Server có số liệu thống kê về các chỉ mục cho biết rằng 1% số hàng khớp với từng vị từ. Nó giả định rằng các thống kê này là độc lập (nghĩa là không tương quan tích cực hoặc tiêu cực) để trung bình một khi nó đã xử lý 1.000 hàng khớp với vị từ đầu tiên, nó sẽ tìm thấy 10 khớp với thứ hai và có thể thoát. (kế hoạch trên thực tế cho thấy 987 chứ không phải 1.000 nhưng đủ gần).
Trong thực tế vì các vị từ có tương quan nghịch, kế hoạch thực tế cho thấy rằng tất cả 200.000 hàng phù hợp cần được xử lý từ mỗi chỉ mục nhưng điều này được giảm nhẹ ở một mức độ nào đó bởi vì các hàng không tham gia cũng có nghĩa là thực sự cần tìm kiếm.
So sánh với
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
Cung cấp cho các kế hoạch dưới đây được chi phí tại 0.567943
Việc thêm ký tự đại diện hiện đã gây ra quét chỉ mục. Chi phí của kế hoạch vẫn còn khá thấp mặc dù để quét trên bảng hàng 20 triệu.
Thêm querytraceon 9130
một số thông tin
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
OPTION (QUERYTRACEON 9130)
Có thể thấy rằng SQL Server cho rằng nó sẽ chỉ cần quét khoảng 100.000 hàng trước khi tìm thấy 10 khớp với vị từ và TOP
có thể dừng yêu cầu các hàng.
Một lần nữa điều này có ý nghĩa với giả định độc lập như 10 * 100 * 100 = 100,000
Cuối cùng, hãy thử và buộc một kế hoạch giao cắt chỉ số
SELECT TOP 10 *
FROM animal WITH (INDEX(ix_species), INDEX(ix_colour))
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
Điều này mang lại cho tôi một kế hoạch song song với chi phí ước tính là 3,4625
Sự khác biệt chính ở đây là colour like 'black%'
vị ngữ có thể khớp với nhiều màu khác nhau. Điều này có nghĩa là các hàng chỉ mục phù hợp cho vị từ đó không còn được đảm bảo để được sắp xếp theo thứ tự id
.
Ví dụ: chỉ mục tìm kiếm like 'black%'
có thể trả về các hàng sau
+------------+----+
| Colour | id |
+------------+----+
| black | 12 |
| black | 20 |
| black | 23 |
| black | 25 |
| blackberry | 1 |
| blackberry | 50 |
+------------+----+
Trong mỗi màu, các id được sắp xếp nhưng các id trên các màu khác nhau có thể không được.
Kết quả là SQL Server không còn có thể thực hiện giao cắt chỉ mục kết hợp hợp nhất (mà không cần thêm toán tử sắp xếp chặn) và thay vào đó, nó sẽ thực hiện phép nối băm. Hash Join đang chặn đầu vào bản dựng, vì vậy bây giờ chi phí phản ánh thực tế là tất cả các hàng khớp sẽ cần được xử lý từ đầu vào bản dựng thay vì giả sử nó sẽ chỉ phải quét 1.000 như trong kế hoạch đầu tiên.
Tuy nhiên, đầu vào thăm dò không chặn và nó vẫn ước tính không chính xác rằng nó sẽ có thể dừng việc thăm dò sau khi xử lý 987 hàng từ đó.
(Thông tin thêm về Không chặn so với chặn lặp ở đây)
Do chi phí gia tăng của các hàng ước tính thêm và hàm băm tham gia quét chỉ mục một phần có vẻ rẻ hơn.
Trong thực tế, quét chỉ mục cụm "một phần" hoàn toàn không phải là một phần và nó cần phải chug qua toàn bộ 20 triệu hàng thay vì 100 nghìn giả định khi so sánh các kế hoạch.
Việc tăng giá trị của TOP
(hoặc loại bỏ hoàn toàn) cuối cùng sẽ gặp một điểm bùng phát trong đó số lượng hàng mà nó ước tính quét CI sẽ cần phải làm cho kế hoạch đó trông đắt hơn và nó trở lại kế hoạch giao cắt chỉ mục. Đối với tôi cắt ra điểm giữa hai phương án là TOP (89)
vs TOP (90)
.
Đối với bạn nó cũng có thể khác nhau vì nó phụ thuộc vào độ rộng của chỉ số cụm.
Loại bỏ TOP
và buộc quét CI
SELECT *
FROM animal WITH (INDEX = 1)
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
Được chi phí 88.0586
trên máy của tôi cho bảng ví dụ của tôi.
Nếu SQL Server nhận thức được rằng sở thú không có thiên nga đen và nó sẽ cần phải quét toàn bộ thay vì chỉ đọc 100.000 hàng thì kế hoạch này sẽ không được chọn.
Tôi đã thử stats cột đa trên animal(species,colour)
và animal(colour,species)
và lọc số liệu thống kê trên animal (colour) where species = 'swan'
nhưng không ai trong số những sự giúp đỡ thuyết phục nó rằng thiên nga đen không tồn tại và TOP 10
quét sẽ cần phải xử lý hơn 100.000 hàng.
Điều này là do "giả định bao gồm" trong đó SQL Server về cơ bản giả định rằng nếu bạn đang tìm kiếm thứ gì đó thì nó có thể tồn tại.
Trên 2008+ có cờ theo dõi tài liệu 4138 tắt mục tiêu hàng. Hiệu quả của việc này là kế hoạch được tính chi phí mà không có giả định rằng TOP
sẽ cho phép các nhà khai thác con chấm dứt sớm mà không cần đọc tất cả các hàng khớp. Với cờ theo dõi này, tôi tự nhiên có được kế hoạch giao chỉ số tối ưu hơn.
SELECT TOP 10 *
FROM animal
WHERE colour LIKE 'black%'
AND species LIKE 'swan'
OPTION (QUERYTRACEON 4138)
Kế hoạch này hiện chi phí chính xác cho việc đọc toàn bộ 200 nghìn hàng trong cả hai chỉ số tìm kiếm nhưng chi phí cao hơn cho việc tra cứu chính (ước tính 2 nghìn so với thực tế 0. Điều TOP 10
này sẽ hạn chế tối đa 10 nhưng cờ theo dõi ngăn điều này được tính đến) . Tuy nhiên, kế hoạch có chi phí rẻ hơn đáng kể so với quét CI đầy đủ vì vậy được chọn.
Tất nhiên kế hoạch này có thể không được tối ưu cho các kết hợp mà là phổ biến. Chẳng hạn như thiên nga trắng.
Một chỉ mục tổng hợp trên animal (colour, species)
hoặc lý tưởng animal (species, colour)
sẽ cho phép truy vấn hiệu quả hơn nhiều cho cả hai kịch bản.
Để sử dụng hiệu quả nhất của chỉ số tổng hợp, LIKE 'swan'
cũng cần phải thay đổi thành = 'swan'
.
Bảng dưới đây cho thấy các vị từ tìm kiếm và các vị từ còn lại được hiển thị trong các kế hoạch thực hiện cho tất cả bốn hoán vị.
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
| WHERE clause | Index | Seek Predicate | Residual Predicate |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
| colour LIKE 'black%' AND species LIKE 'swan' | ix_colour_species | colour >= 'black' AND colour < 'blacL' | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species LIKE 'swan' | ix_species_colour | species >= 'swan' AND species <= 'swan' | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species = 'swan' | ix_colour_species | (colour,species) >= ('black', 'swan')) AND colour < 'blacL' | colour LIKE 'black%' AND species = 'swan' |
| colour LIKE 'black%' AND species = 'swan' | ix_species_colour | species = 'swan' AND (colour >= 'black' and colour < 'blacL') | colour like 'black%' |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
TOP
giá trị trong một biến có nghĩa là nó sẽ giả sửTOP 100
chứ không phảiTOP 10
. Điều này có thể hoặc không thể giúp tùy thuộc vào điểm bùng phát giữa hai kế hoạch là gì.