Tác giả khách mời:Bert Wagner (@bertwagner)
Loại bỏ tham gia là một trong nhiều kỹ thuật mà trình tối ưu hóa truy vấn SQL Server sử dụng để tạo kế hoạch truy vấn hiệu quả. Cụ thể, loại bỏ nối xảy ra khi SQL Server có thể thiết lập bình đẳng bằng cách sử dụng logic truy vấn hoặc các ràng buộc cơ sở dữ liệu đáng tin cậy để loại bỏ các phép nối không cần thiết. Xem phiên bản video đầy đủ của bài đăng này trên kênh YouTube của tôi.
Tham gia hành động loại bỏ
Cách đơn giản nhất để giải thích việc loại bỏ tham gia là thông qua một loạt các bản trình diễn. Đối với những ví dụ này, tôi sẽ sử dụng cơ sở dữ liệu demo WideWorldImporters.
Để bắt đầu mọi thứ, chúng ta sẽ xem xét cách thức hoạt động của tính năng loại bỏ tham gia khi có khóa ngoại:
SELECT il. * FROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
Trong ví dụ này, chúng tôi chỉ trả lại dữ liệu từ Sales.InvoiceLines nơi có một InvoiceID phù hợp được tìm thấy trong Sales.Invoices. Mặc dù bạn có thể mong đợi kế hoạch thực thi hiển thị một toán tử tham gia trên các bảng Sales.InvoiceLines và Sales.Invoices, nhưng SQL Server không bao giờ thấy phiền khi xem Sales.Invoices:
SQL Server tránh tham gia vào bảng Sales.Invoices vì nó tin tưởng tính toàn vẹn tham chiếu được duy trì bởi ràng buộc khóa ngoại được xác định trên InvoiceID giữa Sales.InvoiceLines và Sales.Invoices; nếu một hàng tồn tại trong Sales.InvoiceLines, một hàng có giá trị phù hợp cho InvoiceID phải tồn tại trong Bán hàng.Invoices. Và vì chúng tôi chỉ trả lại dữ liệu từ bảng Sales.InvoiceLines, nên SQL Server không cần đọc bất kỳ trang nào từ Sales.Invoices.
Chúng tôi có thể xác minh rằng SQL Server đang sử dụng ràng buộc khóa ngoại để loại bỏ liên kết bằng cách bỏ ràng buộc và chạy lại truy vấn của chúng tôi:
ALTER TABLE [Bán hàng]. [InvoiceLines] DROP CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];
Không có thông tin về mối quan hệ giữa hai bảng của chúng tôi, SQL Server buộc phải thực hiện phép nối, quét một chỉ mục trên bảng Sales.Invoices của chúng tôi để tìm các InvoiceID phù hợp.
Từ quan điểm I / O, SQL Server phải đọc thêm 124 trang từ một chỉ mục trên bảng Sales.Invoices và điều đó chỉ vì nó có thể sử dụng chỉ mục hẹp (một cột) được tạo bởi một ràng buộc khóa ngoại khác. Kịch bản này có thể diễn ra tồi tệ hơn nhiều trên các bảng lớn hơn hoặc các bảng không được lập chỉ mục thích hợp.
Hạn chế
Trong khi ví dụ trước cho thấy những điều cơ bản về cách hoạt động của loại bỏ tham gia, chúng ta cần lưu ý một số lưu ý.
Đầu tiên, hãy thêm lại ràng buộc khóa ngoại của chúng ta:
ALTER TABLE [Doanh số]. [InvoiceLines] VỚI SỐ LƯU Ý THÊM [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] FOREIGN KEY ([InvoiceID]) TÀI LIỆU THAM KHẢO [Doanh số]. [Hóa đơn] ([InvoiceID]);
Nếu chúng tôi chạy lại truy vấn mẫu của mình, chúng tôi sẽ nhận thấy rằng chúng tôi không nhận được một kế hoạch có biểu hiện loại bỏ tham gia; thay vào đó, chúng tôi nhận được một kế hoạch quét cả hai bảng đã tham gia của chúng tôi.
Lý do điều này xảy ra là vì khi chúng tôi thêm lại ràng buộc khóa ngoại của mình, SQL Server không biết liệu có bất kỳ dữ liệu nào đã được sửa đổi trong thời gian chờ đợi hay không. Mọi dữ liệu mới hoặc dữ liệu đã thay đổi có thể không tuân theo ràng buộc này, vì vậy SQL Server không thể tin tưởng vào tính hợp lệ của dữ liệu của chúng tôi:
SELECT f.name AS Foreign_key_name, OBJECT_NAME (f.parent_object_id) AS table_name, COL_NAME (fc.parent_object_id, fc.parent_column_id) AS bind_column_name, OBJECT_NAME (f.referenced_object_id) AS reference_object, COL_NAME (fc.referenced, COL_NAME (fc.referenced) ) AS reference_column_name, f.is_not_trustedFROM sys.foreign_keys AS f INNER JOIN sys.foreign_key_columns AS fc ON f.object_id =fc.constraint_object_idWHERE f.parent_object_id =OBJECT_ID ('Sales.InvoiceLines');
Để thiết lập lại độ tin cậy của SQL Server về ràng buộc này, chúng ta phải kiểm tra tính hợp lệ của nó:
ALTER TABLE [Bán hàng]. [InvoiceLines] VỚI CHECK CHECK CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];Trên các bảng lớn, thao tác này có thể mất một chút thời gian, chưa kể đến việc SQL Server phải xác thực dữ liệu này trong mỗi lần sửa đổi chèn / cập nhật / xóa về sau.
Một hạn chế khác là SQL Server không thể loại bỏ các bảng đã tham gia khi truy vấn cần trả lại bất kỳ dữ liệu nào từ các ứng cử viên loại bỏ tiềm năng đó:
SELECT il. *, i.InvoiceDateFROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
Loại bỏ tham gia không xảy ra trong truy vấn ở trên vì chúng tôi đang yêu cầu dữ liệu từ Bán hàng. Hóa đơn được trả lại, buộc SQL Server phải đọc dữ liệu từ bảng đó.
Cuối cùng, điều quan trọng cần lưu ý là việc loại bỏ nối sẽ không xảy ra khi khóa ngoại có nhiều cột hoặc nếu các bảng ở trong tempdb. Lý do sau là một trong số những lý do bạn không nên cố gắng giải quyết các vấn đề tối ưu hóa bằng cách sao chép các bảng của mình vào tempdb.
Các tình huống bổ sung
Nhiều bảng
Loại bỏ liên kết không chỉ giới hạn ở các phép nối bên trong hai bảng và các bảng có ràng buộc khóa ngoài.
Ví dụ:chúng ta có thể tạo một bảng bổ sung tham chiếu đến cột Sales.Invoices.InvoiceID:
TẠO BẢNG Sales.InvoiceClickTracking (InvoiceClickTrackingID bigint IDENTITY PRIMARY KEY, InvoiceID int - các trường khác sẽ ở đây); CHUYỂN BẢNG THÊM [Bán hàng]. [InvoiceClickTracking] VỚI KIỂM TRA THÊM CÔNG TÁC [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices] NGOẠI KHÓA ([InvoiceID]) TÀI LIỆU THAM KHẢO [Bán hàng]. [Hóa đơn] ([InvoiceID]);Việc kết hợp bảng này vào truy vấn mẫu ban đầu của chúng tôi cũng sẽ cho phép Máy chủ SQL loại bỏ bảng Bán hàng của chúng tôi.
SELECT il.InvoiceID, ict.InvoiceID FROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID INNER JOIN Sales.InvoiceClickTracking ict ON i.InvoiceID =ict.InvoiceID;
SQL Server có thể loại bỏ bảng Sales.Invoices do sự liên kết bắc cầu giữa các mối quan hệ của các bảng này.
Ràng buộc duy nhất
Thay vì ràng buộc khóa ngoại, SQL Server cũng sẽ thực hiện loại bỏ kết nối nếu nó có thể tin cậy mối quan hệ dữ liệu với một ràng buộc duy nhất:
ALTER TABLE [Bán hàng]. [InvoiceClickTracking] DROP CONSTRAINT [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices]; ĐI ALTER TABLE Sales.InvoiceClickTracking ADD CONSTRAINT UQ_InvoiceID UNIQUE (InvoiceID); ĐI CHỌN i.InvoiceID TỪ Sales.InvoiceClickTracking ict RIGHT THAM GIA Bán hàng.Invoices i ON ict.InvoiceID =i.InvoiceID;
Tham gia bên ngoài
Miễn là SQL Server có thể suy ra các ràng buộc về mối quan hệ, các kiểu kết hợp khác cũng có thể bị loại bỏ bảng. Ví dụ:
SELECT il.InvoiceIDFROM Sales.InvoiceLines il TRÁI THAM GIA Sales.Invoices i ON il.InvoiceID =i.InvoiceIDVì chúng tôi vẫn có ràng buộc khóa ngoại của chúng tôi bắt buộc mọi InvoiceID trong Bán hàng. InvoiceLines phải có một InvoiceID tương ứng trong Bán hàng. Hóa đơn, SQL Server không có vấn đề gì khi trả lại mọi thứ từ Bán hàng.InvoiceLInes mà không cần tham gia vào Bán hàng. Hóa đơn:
Không yêu cầu ràng buộc
Nếu SQL Server có thể đảm bảo rằng nó sẽ không cần dữ liệu từ một bảng nhất định, thì nó có thể loại bỏ một phép nối.
Không có loại bỏ kết hợp nào xảy ra trong truy vấn này bởi vì SQL Server không thể xác định xem mối quan hệ giữa Sales.Invoices và Sales.InvoiceLines là 1-to-1, 1-to-0 hay 1-to-nhiều. Nó buộc phải đọc Sales.InvoiceLines để xác định xem có hàng nào phù hợp được tìm thấy không:
CHỌN i.InvoiceIDFROM Sales.InvoiceLines il QUYỀN THAM GIA Sales.Invoices i ON il.InvoiceID =i.InvoiceID;
Tuy nhiên, nếu chúng tôi chỉ định rằng chúng tôi muốn một bộ DISTINCT gồm i.InvoiceIDs, thì mọi giá trị duy nhất từ Sales.Invoices trả về từ SQL Server bất kể các hàng đó có mối quan hệ gì với Sales.InvoiceLines.
- Chỉ để chứng minh không có khóa ngoại nào đang hoạt động ở đây ALTER TABLE [Bán hàng]. [InvoiceLines] DROP CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices]; ĐI - Kết quả khác biệt của chúng tôi đặtSELECT DISTINCT i.InvoiceIDFROM Sales.InvoiceLines il RIGHT i ON il.InvoiceID =i.InvoiceID;
Lượt xem
Một ưu điểm của loại bỏ liên kết là nó có thể hoạt động với các chế độ xem, ngay cả khi truy vấn chế độ xem cơ bản không thể sử dụng loại bỏ liên kết:
- Thêm lại BẢNG ALTER FK [Bán hàng] của chúng tôi. [InvoiceLines] VỚI CHECK ADD CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] FOREIGN KEY ([InvoiceID]) TÀI LIỆU THAM KHẢO [Bán hàng]. [Hóa đơn] ([InvoiceID]); ĐI - Create chế độ xem của chúng tôi sử dụng truy vấn không thể sử dụng tham gia loại trừCREATE XEM Sales.vInvoicesAndInvoiceLinesAS CHỌN i.InvoiceID, i.InvoiceDate, il.Quantity, il.TaxRate FROM Sales.InvoiceLines il INNER JOIN Sales.Invoices i ON il.InvoiceID =i.InvoiceID; GO - Loại bỏ tham gia hoạt động bởi vì chúng tôi không chọn bất kỳ - cột nào từ bảng Bán hàng bên dưới.
Kết luận
Loại bỏ tham gia là một tối ưu hóa mà SQL Server thực hiện khi nó xác định rằng nó có thể cung cấp một tập hợp kết quả chính xác mà không cần đọc dữ liệu từ tất cả các bảng được chỉ định trong truy vấn đã gửi. Việc tối ưu hóa này có thể cung cấp những cải tiến hiệu suất đáng kể bằng cách giảm số lượng trang SQL Server phải đọc, tuy nhiên, nó thường đi kèm với cái giá phải trả là cần duy trì một số ràng buộc cơ sở dữ liệu nhất định. Chúng tôi có thể cấu trúc lại các truy vấn để đạt được các kế hoạch thực thi đơn giản hơn mà loại bỏ liên kết cung cấp, tuy nhiên, việc có trình tối ưu hóa truy vấn tự động đơn giản hóa các kế hoạch của chúng tôi bằng cách loại bỏ các liên kết không cần thiết là một lợi ích tuyệt vời.
Một lần nữa, tôi mời bạn xem phiên bản video đầy đủ của bài đăng này.
Giới thiệu về tác giả
Bert là một nhà phát triển kinh doanh thông minh đến từ Cleveland, Ohio. Anh ấy thích viết các truy vấn chạy nhanh và thích giúp người khác học cách trở thành người giải quyết vấn đề SQL tự túc. Bert các blog về SQL Server tại bertwagner.com và tạo các video trên YouTube về SQL Server tại youtube.com/c/bertwagner.