Trường hợp này Quét liên tục và Tham gia ngoài trái đến từ đâu trong một kế hoạch truy vấn CHỌN tầm thường?


21

Tôi có bảng này:

CREATE TABLE [dbo].[Accounts] (
    [AccountId] UNIQUEIDENTIFIER UNIQUE NOT NULL DEFAULT NEWID(),
    -- WHATEVER other columns
);
GO
CREATE UNIQUE CLUSTERED INDEX [AccountsIndex]
    ON [dbo].[Accounts]([AccountId] ASC);
GO

Truy vấn này:

DECLARE @result UNIQUEIDENTIFIER
SELECT @result = AccountId FROM Accounts WHERE AccountId='guid-here'

thực hiện với một kế hoạch truy vấn bao gồm một Tìm kiếm chỉ mục duy nhất - như mong đợi:

SELECT <---- Clustered Index Seek

Truy vấn này thực hiện tương tự:

DECLARE @result UNIQUEIDENTIFIER
SET @result = (SELECT AccountId FROM Accounts WHERE AccountId='guid-here')

nhưng nó được thực hiện với một kế hoạch trong đó kết quả của Index Seek bị bỏ lại bên ngoài Tham gia với kết quả của một số Quét liên tục và sau đó được đưa vào tính toán vô hướng:

SELECT <--- Compute Scalar <--- Left Outer Join <--- Constant Scan
                                      ^
                                      |------Clustered Index Seek

Phép thuật thêm đó là gì? Constant Scan theo sau bởi Left Outer Join làm gì?

Câu trả lời:


29

Các ngữ nghĩa của hai tuyên bố là khác nhau:

  • Đầu tiên không đặt giá trị của biến nếu không tìm thấy hàng.
  • Thứ hai luôn đặt biến, bao gồm thành null nếu không tìm thấy hàng nào.

Quét liên tục tạo ra một hàng trống (không có cột!) Sẽ dẫn đến biến được cập nhật trong trường hợp không có gì khớp với bảng cơ sở. Tham gia bên trái đảm bảo hàng trống tồn tại tham gia. Việc gán biến có thể được coi là xảy ra tại nút gốc của kế hoạch thực hiện.

Sử dụng SELECT @result

-- Set initial value
DECLARE @result uniqueidentifier = {guid 'FE2CA909-1162-4C6C-A7AC-33B257E28539'};

-- @result does not change
SELECT @result = AccountId 
FROM Accounts 
WHERE AccountId={guid '7AD4D33C-1ED7-4183-B7F3-48C33D666525'};

SELECT @result;

Kết quả 1

Sử dụng SET @result

-- Set initial value
DECLARE @result uniqueidentifier = {guid 'FE2CA909-1162-4C6C-A7AC-33B257E28539'};

-- @result set to null
SET @result = 
(
    SELECT AccountId 
    FROM Accounts 
    WHERE AccountId={guid '7AD4D33C-1ED7-4183-B7F3-48C33D666525'}
);

SELECT @result;

Kết quả 2

Kế hoạch thực hiện

Nhiệm vụ CHỌNKhông có hàng đến nút gốc, vì vậy không có sự phân công nào xảy ra.

Phân công SETMột hàng luôn đến nút gốc, do đó việc gán biến xảy ra.


Việc quét liên tục thêm và các vòng lặp lồng nhau bên ngoài Tham gia không có gì đáng quan tâm. Sự tham gia đặc biệt rẻ vì nó được đảm bảo gặp một hàng ở đầu vào bên ngoài của nó và nhiều nhất là một hàng (trong ví dụ của bạn) ở đầu vào bên trong.

Có nhiều cách khác để đảm bảo một hàng được tạo từ truy vấn con để đảm bảo việc gán biến xảy ra. Một là sử dụng tổng hợp vô hướng dự phòng (không có nhóm theo mệnh đề):

-- Set initial value
DECLARE @result uniqueidentifier = {guid 'FE2CA909-1162-4C6C-A7AC-33B257E28539'};

-- @result set to null
SET @result = 
    (
        SELECT MAX(AccountId)
        FROM Accounts 
        WHERE AccountId={guid '7AD4D33C-1ED7-4183-B7F3-48C33D666525'} 
    );
SELECT @result;

Kết quả 3

Kế hoạch thực hiện tổng hợp vô hướng

Lưu ý tổng hợp vô hướng tạo ra một hàng ngay cả khi nó không nhận được đầu vào.

Tài liệu:

Nếu câu lệnh SELECT trả về không có hàng, biến vẫn giữ giá trị hiện tại của nó. Nếu biểu thức là truy vấn con vô hướng không trả về giá trị, biến được đặt thành NULL.

Để gán biến, chúng tôi khuyên bạn nên sử dụng SET @local_variable thay vì SELECT @local_variable.

Đọc thêm:

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.