Chỉ mục được lọc chỉ được sử dụng khi phần được lọc nằm trong THAM GIA chứ không phải WHERE


10

Tôi đã tạo chỉ mục được lọc bên dưới, tuy nhiên khi tôi chạy 2 truy vấn xuống thì chỉ mục này chỉ được sử dụng cho tìm kiếm trong ví dụ đầu tiên có END_DTTM trong THAM GIA chứ không phải là mệnh đề where (đó là điểm khác biệt duy nhất trong các truy vấn) . Bất cứ ai có thể giải thích tại sao điều này xảy ra?

Tạo chỉ mục

CREATE NONCLUSTERED INDEX [ix_PATIENT_LIST_BESPOKE_LIST_ID_includes] ON [dbo].[PATIENT_LIST_BESPOKE] 
(
    [LIST_ID] ASC,
    [END_DTTM] ASC
)
WHERE ([END_DTTM] IS NULL)

Truy vấn

DECLARE @LIST_ID INT = 3655

--This one seeks on the index

SELECT  
    PATIENT_LISTS.LIST_ID
FROM    
    DBO.PATIENT_LISTS
    LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID  
                                      AND PATIENT_LIST_BESPOKE.END_DTTM IS NULL
WHERE
    PATIENT_LISTS.LIST_ID = @LIST_ID

--This one scans on the index

SELECT  
    PATIENT_LISTS.LIST_ID
FROM    
    DBO.PATIENT_LISTS
    LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID  
WHERE   
    PATIENT_LISTS.LIST_ID = @LIST_ID AND
    PATIENT_LIST_BESPOKE.END_DTTM IS NULL   

Câu trả lời:


12

Để trình tối ưu hóa khớp với một vị từ với một chỉ mục (được lọc hoặc theo cách khác), vị từ phải xuất hiện liền kề với hoạt động Nhận trong cây truy vấn logic. Để tạo điều kiện cho điều này, các biến vị ngữ thường được đẩy càng gần càng tốt với các lá của cây logic trước khi tối ưu hóa bắt đầu.

Để đơn giản hóa đáng kể, việc thực hiện chiến lược chỉ số vật lý thực hiện điều này:

Predicate + Logical Get -> Physical Get (using Index)

Truy vấn bạn quan tâm bắt đầu với vị từ trên một phép nối ngoài:

Predicate on T2 --+-- LOJ -- Get (T1)
                       |
                       +---- Get (T2)

Hình dạng này không khớp với quy tắc chiến lược chỉ mục vì vị từ không liền kề với Get. Vì vậy, phần đầu tiên của câu trả lời là khớp chỉ mục được lọc sẽ thất bại trừ khi vị từ có thể được đẩy qua phép nối ngoài.

Phần thứ hai đơn giản là trình tối ưu hóa không chứa quy tắc thăm dò cần thiết để di chuyển một vị từ qua một phép nối bên ngoài ở phía được bảo toàn, bởi vì phép biến đổi rất hiếm khi hợp lệ. Đây là một tính năng chung của trình tối ưu hóa mà chỉ các quy tắc hữu ích thường xuyên nhất được thực hiện.

Kết quả là, khớp chỉ mục được lọc không thành công trong trường hợp này. Để rõ ràng, viết lại sẽ hợp lệ trong trường hợp rất cụ thể mà bạn trích dẫn (truy vấn thứ hai).

Đối với dạng truy vấn đầu tiên (với các ngữ nghĩa khác nhau), vị từ được liên kết với phép nối từ đầu và logic đẩy xuống vị ngữ có thể di chuyển khoảng cách ngắn này đến Get vì nó không phải di chuyển qua phép nối ngoài như giải thích ở trên.

Bối cảnh và thông tin thêm:


9

Đây không phải là các truy vấn giống nhau về mặt ngữ nghĩa, vì người ta có thể lọc trước khi tham gia người kia có thể lọc sau. Hãy để tôi minh họa bằng một ví dụ đơn giản hơn:

CREATE TABLE dbo.Lefty(LeftyID INT PRIMARY KEY);

CREATE TABLE dbo.Righty(LeftyID INT, SomeList INT);

INSERT dbo.Lefty(LeftyID) VALUES(1),(2),(3);

INSERT dbo.Righty(LeftyID, SomeList) VALUES(1,1),(1,NULL),(2,2);

Truy vấn 1 trả về cả ba hàng:

SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
AND r.SomeList IS NULL;

Truy vấn 2, tuy nhiên, bỏ LeftyID 2:

SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
WHERE r.SomeList IS NULL;

Bằng chứng SQLfiddle

Nếu bạn đang cố gắng thực hiện một liên kết chống bán, cột được kiểm tra cần phải không có giá trị . Các tiêu chí di chuyển giữa ON và WHERE không tạo ra sự khác biệt logic khi bạn chỉ giao dịch với INNER, nhưng với OUTER thì có một sự khác biệt đáng kể. Và bạn nên quan tâm nhiều hơn rằng kết quả của bạn là chính xác hơn là liệu chỉ số được lọc có thể được sử dụng hay không.


cảm ơn vì câu trả lời nhưng tôi không khẳng định các truy vấn là như nhau, tôi hỏi tại sao một truy vấn sử dụng chỉ mục được lọc và truy vấn kia thì không.
chris

@chris Bạn đã thử ép chỉ mục đó bằng một gợi ý chỉ mục? Tôi tò mò muốn so sánh các kế hoạch thực hiện, hậu thực hiện với và không có gợi ý đó. Đối với tôi rõ ràng là trình tối ưu hóa đã bỏ qua chỉ số đó khi tin rằng nó đang thực hiện chống tham gia (vì nó sẽ không mong đợi một cột nullable được sử dụng trong trường hợp đó), nhưng tôi không chắc liệu đó có phải là thực hiện với chi phí hoặc thứ tự các hoạt động hoặc một số kiến ​​thức cơ bản rằng có khả năng có nhiều hàng đến từ phía bên trái hơn so với các hàng nằm trong chỉ mục được lọc. Xem các kế hoạch có thể giúp đỡ.
Aaron Bertrand

3

Hai truy vấn là khác nhau - về ý nghĩa và kết quả. Đây là một cách viết lại, vì vậy rõ ràng hơn những gì hai truy vấn đang làm:

-- 1st query
SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
    LEFT JOIN  
      ( SELECT LIST_ID                    -- the filtered index
        FROM   DBO.PATIENT_LIST_BESPOKE   -- can be used
        WHERE  END_DTTM IS NULL           -- for the subquery
      ) AS b
    ON  a.LIST_ID = b.LIST_ID ;           -- and the join

và thứ 2:

-- 2nd query
SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
    JOIN  
      ( SELECT LIST_ID                    -- the filtered index
        FROM   DBO.PATIENT_LIST_BESPOKE   -- can be used
        WHERE  END_DTTM IS NULL           -- for the subquery
      ) AS b
    ON  a.LIST_ID = b.LIST_ID             -- and the join

UNION ALL

SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
WHERE NOT EXISTS  
      ( SELECT *
        FROM   DBO.PATIENT_LIST_BESPOKE AS b
        WHERE  a.LIST_ID = b.LIST_ID         -- but not for this
      ) ;

Tôi nghĩ bây giờ khá rõ ràng rằng đối với phần 2 của truy vấn 2nq, chỉ mục được lọc có thể được sử dụng.


Cụ thể, liên quan đến các truy vấn này, có 4 loại LIST_IDgiá trị trong bảng đầu tiên:

  • (a) các giá trị có các hàng khớp trong bảng thứ hai, tất cả đều có END_DTTM IS NULL.

  • (b) các giá trị có các hàng khớp trong bảng thứ hai, cả với END_DTTM IS NULLvà với END_DTTM IS NOT NULL.

  • (c) các giá trị có các hàng khớp trong bảng thứ hai, tất cả đều có END_DTTM IS NOT NULL.

  • (d) các giá trị không có hàng khớp trong bảng thứ hai.

Bây giờ, truy vấn thứ nhất sẽ trả về tất cả các giá trị của loại (a) và (b) có thể nhiều lần (nhiều như chúng có một hàng khớp trong bảng thứ hai với END_DTTM IS NULL) và tất cả các hàng loại (c) và (d) chính xác một lần ( đó là phần không khớp của phép nối ngoài).

Truy vấn thứ 2 sẽ trả về tất cả các giá trị của loại (a) và (b) có thể nhiều lần (nhiều như chúng có một hàng khớp trong bảng thứ hai với END_DTTM IS NULL) và tất cả các hàng loại (d) chính xác một lần.
Nó sẽ không trả về bất kỳ giá trị nào của loại (c) vì phép nối sẽ tìm các hàng khớp trong bảng thứ hai (nhưng chúng sẽ có END_DTTM IS NOT NULL) và chúng sẽ bị xóa bởi WHEREmệnh đề tiếp theo .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.