2013-05-08 26 views
9

Tôi có một danh mục văn bản miễn phí trên một bảng đơn giản trên SQL Server 2008R2:Kết hợp tìm kiếm văn bản miễn phí với điều kiện khác là chậm

CREATE FULLTEXT CATALOG customer_catalog; 
CREATE FULLTEXT INDEX ON customer 
( 
    name1 
) 
    KEY INDEX customer_pk 
    ON customer_catalog; 
ALTER FULLTEXT INDEX ON customer START UPDATE POPULATION; 

Nếu tôi thực hiện ba truy vấn sau đây hai trở lại đầu tiên gần như ngay lập tức, trong khi người cuối cùng mất ~ 14 giây trên một bảng với 100.000 hồ sơ:

SELECT 
     customer_id 
    FROM 
     customer 
    WHERE 
     CONTAINS(customer.*, 'nomatch'); 

SELECT 
     customer_id 
    FROM 
     customer 
    WHERE 
     customer.customer_id = 0; 

SELECT 
     customer_id 
    FROM 
     customer 
    WHERE 
     CONTAINS(customer.*, 'nomatch') 
      OR customer.customer_id = 0; 

Sau đây là các queryplans:

enter image description here

Tại sao truy vấn thứ ba chậm hơn nhiều? Tôi có thể làm bất cứ điều gì để cải thiện nó hay tôi cần phải phân chia truy vấn?

+0

Thường viết lại các truy vấn 'CONTAINS' vào' CONTAINSTABLE' sắp xếp loại vấn đề này. [Ví dụ như trong câu trả lời này] (http://stackoverflow.com/questions/2906812/adding-more-or-searches-with-contains-brings-query-to-crawl/2907331#2907331) –

+1

Tùy thuộc vào 2008R2 SP của bạn phiên bản, sự cố của bạn có thể liên quan đến vấn đề MS Connect sau đây: http://connect.microsoft.com/SQLServer/feedback/details/520653/full-text-performance-with-mixed-queries – MicSim

+0

@MicSim: Nếu bạn thực hiện điều đó vào một câu trả lời, tôi sẽ chấp nhận nó. Trong khi các câu trả lời khác đã cung cấp cách giải quyết tốt đẹp, bạn trông giống như câu trả lời thực sự. Cảm ơn! –

Trả lời

2

Tùy thuộc vào phiên bản gói phiên bản MS SQL 2008 R2 của bạn, sự cố của bạn có thể liên quan đến sự cố Microsoft Connect sau đây: Full-text performance with "mixed queries"

Theo mục nhập MS Connect, sự cố sẽ biến mất sau khi cài đặt gói Cập Nhật tích luỹ mới nhất cho SQL Server 2008 R2.

3

Thật khó để nói tại sao, nhưng có vẻ như SQL Server đang chọn một kế hoạch truy vấn không hiệu quả. Dưới đây là một số gợi ý:

Cập nhật số liệu thống kê trên bảng:

UPDATE STATISTICS dbo.customer 

Khi số liệu thống kê được cập nhật, bạn có thể thử truy vấn của bạn một lần nữa và xem nếu có cải tiến.

Điều gì đó khác cho câu lệnh kết hợp OR, SQL Server đang sử dụng quét chỉ mục thay vì tìm kiếm. Bạn có thể thử các FORCESEEK gợi ý và xem nếu mà làm cho một sự khác biệt:

SELECT customer_id 
FROM customer WITH (FORCESEEK) 
WHERE CONTAINS(customer.*, 'nomatch') 
OR customer.customer_id = 0; 

Một lựa chọn khác, như bạn đã đề cập, là để phân chia các báo cáo. sau UNION dụng giống cũng như hai câu lệnh đầu tiên bạn kết hợp:

SELECT customer_id FROM customer 
WHERE CONTAINS(customer.*, 'nomatch') 

UNION 

SELECT customer_id FROM customer 
WHERE customer.customer_id = 0 

Cập nhật - thay đổi trên truy vấn để UNION thay vì UNION ALL.

Như @PondLife đã nêu trong các nhận xét, tôi muốn thực hiện một số UNION trong truy vấn trên thay vì UNION ALL. Sau khi suy nghĩ về nó, tôi cũng đã thử với UNION ALL và nó dường như nhanh hơn. Điều này giả định bạn không quan tâm đến các ID trùng lặp:

SELECT customer_id FROM customer 
WHERE CONTAINS(customer.*, 'nomatch') 

UNION ALL 

SELECT customer_id FROM customer 
WHERE customer.customer_id = 0 
+0

Tôi nghĩ bạn có nghĩa là 'UNION', không phải 'UNION ALL' (trong trường hợp cụ thể này), nếu không một hàng có chứa' nomatch '* và * có ID 0 sẽ xuất hiện hai lần trong tập kết quả thay vì một lần. – Pondlife

+0

@Pondlife - bạn chính xác, tôi sẽ cập nhật. Cảm ơn. –

3

Các "OR" điều kiện logicial thường làm cho các truy vấn chạy rất chậm:/ Thông thường, lựa chọn tốt nhất là sử dụng UNION (ALL).

Trong trường hợp của bạn, tôi khá tò mò về cách sử dụng bạn thực hiện các

SELECT 
    customer_id 
FROM 
    customer 
WHERE 
    customer.customer_id = 0; 

Nó sẽ chỉ dẫn đến một danh sách (có thể rỗng) của số không. Có phải đếm (!) Có bao nhiêu khách hàng có id = 0 không? Có phải để kiểm tra xem khách hàng nào có id là 0 không?

Nếu nó không phải để đếm zero nhưng để biết được họ đang bất kỳ, sau đó truy vấn này nên hiệu quả:

SELECT 
    customer_id 
FROM 
    customer 
WHERE 
    CONTAINS(customer.*, 'nomatch') 
    AND customer.customer_id <> 0 
UNION ALL 
SELECT TOP(1) 
    0 
FROM 
    customer 
WHERE 
    customer.customer_id = 0 

nếu không truy vấn hiệu quả là thế này một:

SELECT 
    customer_id 
FROM 
    customer 
WHERE 
    CONTAINS(customer.*, 'nomatch') 
    AND customer.customer_id <> 0 
UNION ALL 
SELECT 
    0 
FROM 
    customer 
WHERE 
    customer.customer_id = 0 

(Tôi chỉ cần loại bỏ mệnh đề TOP)

+0

'customer_id = 0' chỉ để hiển thị một ví dụ đơn giản. Truy vấn thực tế là một kết hợp của hai bảng có CONTAINS (table1. *) OR CONTAINS (bảng2. *). Nhưng tôi nhận ra rằng tôi có thể tái tạo vấn đề chỉ với một truy vấn trên id-cột, vì vậy tôi nghĩ rằng đó sẽ là một ví dụ đơn giản hơn. –

+0

Điều kiện không quan trọng, hãy sử dụng truy vấn thứ hai của tôi. Bắt đầu với phần sử dụng chỉ mục văn bản đầy đủ và lọc nó để loại trừ phần thứ hai (trong ví dụ này bằng cách sử dụng customer.customer_id <> 0), sau đó tạo một liên minh ở phần thứ hai. – Serge

Các vấn đề liên quan