11

Tôi có một proc được lưu trữ để tìm kiếm các sản phẩm (250.000 hàng) bằng cách sử dụng chỉ mục văn bản đầy đủ.Tại sao màn trình diễn của 2 truy vấn này lại khác biệt?

proc được lưu trữ có tham số là điều kiện tìm kiếm toàn văn bản. Tham số này có thể là null, vì vậy tôi đã thêm một kiểm tra rỗng và truy vấn đột nhiên bắt đầu chạy các đơn đặt hàng có cường độ chậm hơn.

-- This is normally a parameter of my stored proc 
DECLARE @Filter VARCHAR(100) 
SET @Filter = 'FORMSOF(INFLECTIONAL, robe)' 

-- #1 - Runs < 1 sec 
SELECT TOP 100 ID FROM dbo.Products 
WHERE CONTAINS(Name, @Filter) 

-- #2 - Runs in 18 secs 
SELECT TOP 100 ID FROM dbo.Products 
WHERE @Filter IS NULL OR CONTAINS(Name, @Filter) 

Sau đây là các kế hoạch thực hiện:

Query # 1 Execution plant #1

Query # 2 Execution plant #2

Tôi phải thừa nhận rằng tôi không phải là rất quen thuộc với kế hoạch thực hiện. Sự khác biệt rõ ràng duy nhất với tôi là sự gia nhập là khác nhau. Tôi sẽ thử thêm một gợi ý nhưng không tham gia vào truy vấn của tôi Tôi không chắc chắn làm thế nào để làm điều đó.

Tôi cũng không hiểu tại sao chỉ mục được gọi là IX_SectionID được sử dụng, vì đó là chỉ mục chỉ chứa cột SectionID và cột đó không được sử dụng ở bất kỳ đâu.

Trả lời

8

OR có thể đè bẹp hiệu suất, vì vậy làm điều đó theo cách này:

DECLARE @Filter VARCHAR(100) 
SET @Filter = 'FORMSOF(INFLECTIONAL, robe)' 

IF @Filter IS NOT NULL 
BEGIN 
    SELECT TOP 100 ID FROM dbo.Products 
    WHERE CONTAINS(Name, @Filter) 
END 
ELSE 
BEGIN 
    SELECT TOP 100 ID FROM dbo.Products 
END 

Nhìn vào bài viết này: Dynamic Search Conditions in T-SQL by Erland Sommarskog và câu hỏi này: SQL Server 2008 - Conditional Query.

+0

Nice bài viết - thêm 'OPTION (biên dịch lại) 'thực sự giải quyết vấn đề hiệu suất trên các truy vấn thứ 2 (tuy nhiên một vấn đề khác là' CHỨA()' tăng lương một lỗi khi tham số là NULL, nhưng đó là một vấn đề khác). –

1

Bạn đã giới thiệu một điều kiện OR. Trong hầu hết các trường hợp, chỉ đơn giản là kiểm tra rõ ràng hơn đối với NULL và thực hiện một truy vấn so với phương thức của bạn.

Ví dụ thử điều này:

IF @Filter IS NULL 
BEGIN 
SELECT TOP 100 ID FROM dbo.Products 
END 
ELSE 
BEGIN 
SELECT TOP 100 ID FROM dbo.Products 
WHERE @Filter CONTAINS(Name, @Filter) 
END 
3

Kế hoạch truy vấn đầu tiên trông đơn giản:

  1. một tìm kiếm văn bản đầy đủ để giải quyết CONTAINS(Name, @Filter)
  2. một chỉ số quét để tìm kiếm các cột khác của các hàng phù hợp
  3. kết hợp cả hai bằng cách sử dụng băm tham gia

Các concatenation operator tạo thành một liên minh của hai tập bản ghi. Vì vậy, nó trông giống như truy vấn thứ hai được thực hiện:

  1. một chỉ số quét (sau này sử dụng để tìm kiếm các cột khác)
  2. quét liên tục. Tôi cho rằng nó xử lý truy vấn của bạn không được tham số hóa, vì vậy kế hoạch truy vấn không phải làm việc cho bất kỳ giá trị nào khác của @Filter. Nếu chính xác, quét liên tục sẽ giải quyết @Filter is not null.
  3. một tìm kiếm văn bản đầy đủ để giải quyết CONTAINS(Name, @Filter)
  4. đoàn thể là kết quả của 3 với tập rỗng từ 2
  5. loop tham gia là kết quả của 1 và 4 để tìm kiếm các cột khác

Một băm tham gia giao dịch bộ nhớ cho tốc độ; nếu hệ thống của bạn có đủ bộ nhớ, nó nhanh hơn nhiều so với kết nối vòng lặp. Điều này có thể dễ dàng giải thích sự chậm trễ 10-100x.

Một sửa chữa là sử dụng hai truy vấn riêng biệt:

if @Filter is null 
    SELECT TOP 100 ID FROM dbo.Products 
else 
    SELECT TOP 100 ID FROM dbo.Products WHERE CONTAINS(Name, @Filter) 
+0

Thú vị - có cách nào để buộc băm tham gia vào truy vấn thứ hai không? –

+0

Vâng, bạn có thể viết lại nó bằng cách sử dụng 'internal hash join', nhưng điều đó sẽ khá phức tạp. Sẽ tốt hơn nếu bạn sử dụng một trong các giải pháp từ bài viết của Sommarskog. – Andomar