Kế hoạch truy vấn lạ khi sử dụng mệnh đề OR trong THAM GIA - Quét liên tục cho mọi hàng trong bảng


10

Tôi đang cố gắng tạo một kế hoạch truy vấn mẫu để cho thấy tại sao UNIONing hai tập kết quả có thể tốt hơn so với sử dụng OR trong mệnh đề THAM GIA. Một kế hoạch truy vấn tôi đã viết đã làm tôi bối rối. Tôi đang sử dụng cơ sở dữ liệu StackOverflow với chỉ mục không bao gồm trên Users.Reputing.

Hình ảnh kế hoạch truy vấn Truy vấn là

CREATE NONCLUSTERED INDEX IX_NC_REPUTATION ON dbo.USERS(Reputation)
SELECT DISTINCT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts  
    ON Users.Id = Posts.OwnerUserId
    OR Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5

Gói truy vấn có tại https://www.brentozar.com/pastetheplan/?id=BkpZU1MZE , thời lượng truy vấn đối với tôi là 4:37 phút, trả về 26612 hàng.

Tôi chưa từng thấy kiểu quét liên tục này được tạo từ một bảng hiện có trước đây - Tôi không quen tại sao lại có quét liên tục cho mỗi hàng, khi quét thường xuyên được sử dụng cho một hàng được người dùng nhập vào ví dụ CHỌN GETDATE (). Tại sao nó được sử dụng ở đây? Tôi thực sự sẽ đánh giá cao một số hướng dẫn trong việc đọc kế hoạch truy vấn này.

Nếu tôi tách HOẶC đó thành UNION, nó sẽ tạo ra một gói tiêu chuẩn chạy trong 12 giây với cùng 26612 hàng được trả về.

SELECT Users.Id
FROM dbo.Users
    INNER JOIN dbo.Posts
       ON Users.Id = Posts.OwnerUserId
WHERE Users.Reputation = 5
UNION 
SELECT Users.Id
FROM dbo.Users
    INNER JOIN dbo.Posts
       ON  Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5

Tôi giải thích kế hoạch này là làm điều này:

  • Nhận tất cả 41782500 hàng từ Bài viết (số lượng hàng thực tế khớp với quét CI trên Bài đăng)
  • Đối với mỗi 41782500 hàng trong Bài viết:
    • Sản xuất vô hướng:
    • Expr1005: Chủ sở hữuUserId
    • Expr1006: Chủ sở hữuUserId
    • Expr1004: Giá trị tĩnh 62
    • Expr1008: LastEditorUserId
    • Expr1009: LastEditorUserId
    • Expr1007: Giá trị tĩnh 62
  • Trong phần kết hợp:
    • Exp1010: Nếu Expr1005 (Chủ sở hữuUserId) không rỗng, hãy sử dụng Expr1008 (LastEditorUserID) khác
    • Expr1011: Nếu Expr1006 (Chủ sở hữuUserId) không rỗng, hãy sử dụng điều đó, sử dụng Expr1009 (LastEditorUserId)
    • Expr1012: Nếu Expr1004 (62) là null, hãy sử dụng Expr1007 (62)
  • Trong vô hướng tính toán: Tôi không biết ampersand làm gì.
    • Expr1013: 4 [và?] 62 (Expr1012) = 4 và Chủ sở hữuUserId là NULL (NULL = Expr1010)
    • Expr1014: 4 [và?] 62 (Expr1012)
    • Expr1015: 16 và 62 (Expr1012)
  • Theo thứ tự Sắp xếp theo:
    • Expr1013 Desc
    • Expr1014
    • Expr1010
    • Expr1015 Desc
  • Trong Khoảng thời gian hợp nhất, nó đã loại bỏ Expr1013 và Expr1015 (đây là các đầu vào nhưng không phải là đầu ra)
  • Trong Chỉ mục tìm kiếm bên dưới các vòng lặp lồng nhau, nó sử dụng Expr1010 và Expr1011 làm vị ngữ tìm kiếm, nhưng tôi không hiểu làm thế nào nó có quyền truy cập vào các vòng lặp này khi nó không thực hiện nối vòng lặp lồng nhau từ IX_NC_REPUTATION đến cây con có chứa Expr1010 và Expr1011 .
  • Các vòng lặp Nested Loops chỉ trả về các Users.ID có khớp trong phần phụ trước đó. Do đẩy xuống vị ngữ, tất cả các hàng được trả về từ chỉ mục tìm kiếm trên IX_NC_REPUTATION được trả về.
  • Các vòng lặp lồng nhau cuối cùng tham gia: Đối với mỗi bản ghi Bài đăng, hãy xuất Users.Id nơi tìm thấy kết quả khớp trong tập dữ liệu bên dưới.

Bạn đã thử với một truy vấn con hoặc truy vấn con EXISTS chưa? SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND ( EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.OwnerUserId) OR EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.LastEditorUserId) ) ;
ypercubeᵀᴹ

một truy vấn con:SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id IN (Posts.OwnerUserId, Posts.LastEditorUserId) ) ;
ypercubeᵀᴹ 27/12/18

Câu trả lời:


10

Kế hoạch tương tự như kế hoạch tôi đi vào chi tiết hơn ở đây .

Các Postsbảng sẽ được quét.

Đối với mỗi hàng, nó trích xuất OwnerUserIdLastEditorUserId. Đây là một cách tương tự như cách UNPIVOTlàm việc. Bạn thấy một toán tử quét không đổi duy nhất trong kế hoạch cho bên dưới tạo hai hàng đầu ra cho mỗi hàng đầu vào.

SELECT *
FROM dbo.Posts
UNPIVOT (X FOR U IN (OwnerUserId,LastEditorUserId)) Unpvt

Trong trường hợp này, kế hoạch phức tạp hơn một chút vì ngữ nghĩa orlà nếu cả hai giá trị cột giống nhau thì chỉ nên phát ra một hàng từ liên kết trên Users(không phải hai)

Sau đó, chúng được đặt trong khoảng thời gian hợp nhất để trong trường hợp các giá trị giống nhau, phạm vi được thu gọn lại và chỉ có một tìm kiếm được thực hiện đối với Users- nếu không, hai tìm kiếm được thực hiện đối với nó.

Giá trị 62là một lá cờ có nghĩa là tìm kiếm nên là một tìm kiếm bình đẳng.

Về

Tôi không hiểu làm thế nào nó có quyền truy cập vào những cái này khi nó chưa thực hiện phép nối vòng lặp lồng nhau từ IX_NC_REPUTATION đến cây con chứa Expr1010 và Expr1011

Chúng được định nghĩa trong toán tử ghép nối được tô sáng màu vàng. Đây là ở phía bên ngoài của các vòng lặp lồng màu vàng nổi bật. Vì vậy, điều này chạy trước khi tìm kiếm nổi bật màu vàng ở bên trong các vòng lặp lồng nhau.

nhập mô tả hình ảnh ở đây

Một bản viết lại đưa ra một kế hoạch tương tự (mặc dù với khoảng thời gian hợp nhất được thay thế bằng một liên kết hợp nhất) dưới đây trong trường hợp điều này có ích.

SELECT DISTINCT D2.UserId
FROM   dbo.Posts p
       CROSS APPLY (SELECT Users.Id AS UserId
                    FROM   (SELECT p.OwnerUserId
                            UNION /*collapse duplicate to single row*/
                            SELECT p.LastEditorUserId) D1(UserId)
                           JOIN Users
                             ON Users.Id = D1.UserId) D2
OPTION (FORCE ORDER) 

nhập mô tả hình ảnh ở đây

Phụ thuộc vào chỉ mục nào có sẵn trên Postsbảng, một biến thể của truy vấn này có thể hiệu quả hơn UNION ALLgiải pháp đề xuất của bạn . (bản sao của cơ sở dữ liệu tôi không có chỉ mục hữu ích cho việc này và giải pháp được đề xuất thực hiện hai lần quét đầy đủ Posts. Dưới đây thực hiện trong một lần quét)

WITH Unpivoted AS
(
SELECT UserId
FROM dbo.Posts
UNPIVOT (UserId FOR U IN (OwnerUserId,LastEditorUserId)) Unpivoted
)
SELECT DISTINCT Users.Id
FROM dbo.Users INNER HASH JOIN Unpivoted
       ON  Users.Id = Unpivoted.UserId
WHERE Users.Reputation = 5

nhập mô tả hình ảnh ở đây

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.