Kết quả bất ngờ với số ngẫu nhiên và các loại tham gia


16

Tôi có một tập lệnh đơn giản nhận được bốn số ngẫu nhiên (1 đến 4) và sau đó tham gia lại để lấy số cơ sở dữ liệu phù hợp. Khi tôi chạy tập lệnh với THAM GIA TRÁI PHIẾU, tôi nhận được bốn hàng mỗi lần (kết quả mong đợi). Tuy nhiên, khi tôi chạy nó với INNER THAM GIA, tôi nhận được một số lượng hàng khác nhau - đôi khi là hai, đôi khi là tám.

Về mặt logic, không nên có bất kỳ sự khác biệt nào vì tôi biết các hàng có cơ sở dữ liệu 1-4 tồn tại trong sys.database. Và bởi vì chúng tôi đang chọn từ bảng số ngẫu nhiên có bốn hàng (trái ngược với việc tham gia vào bảng đó), không bao giờ có hơn bốn hàng được trả về.

Điều này xảy ra trong cả SQL Server 2012 và 2014. Điều gì khiến INNER THAM GIA trả về số lượng hàng khác nhau?

/* Works as expected -- always four rows */

SELECT rando.RandomNumber, d.database_id
FROM 
  (SELECT 1 + ABS(CHECKSUM(NEWID())) % (4) AS RandomNumber 
   FROM sys.databases WHERE database_id <= 4) AS rando
LEFT JOIN sys.databases d ON rando.RandomNumber = d.database_id;


/* Returns a varying number of rows */

SELECT rando.RandomNumber, d.database_id
FROM 
  (SELECT 1 + ABS(CHECKSUM(NEWID())) % (4) AS RandomNumber 
   FROM sys.databases WHERE database_id <= 4) AS rando
INNER JOIN sys.databases d ON rando.RandomNumber = d.database_id;

/* Also returns a varying number of rows */

WITH rando AS (
  SELECT 1 + ABS(CHECKSUM(NEWID())) % (4) AS RandomNumber
  FROM sys.databases WHERE database_id <= 4
)

SELECT r.RandomNumber, d.database_id
FROM rando AS r
INNER JOIN sys.databases d ON r.RandomNumber = d.database_id;

3
Một cách khác để luôn luôn có 4 hàng: SELECT TOP (4) d.database_id FROM sys.databases AS d CROSS JOIN (VALUES (1),(2),(3),(4)) AS multi (i) WHERE d.database_id <= 4 ORDER BY CHECKSUM(NEWID()) ;Tôi đoán nó hoạt động tốt vì không có tham gia vào giá trị của hàm không xác định.
ypercubeᵀᴹ

Câu trả lời:


9

Bằng cách thêm CHỌN bổ sung, nó sẽ đẩy đánh giá vô hướng tính toán sâu hơn vào kế hoạch và đưa ra vị từ tham gia, vô hướng tính toán ở trên cùng sau đó tham chiếu cái trước đó.

SELECT rando.RandomNumber, d.database_id
FROM 
  (SELECT ( SELECT 1 + ABS(CHECKSUM(NEWID())) % (4)) AS RandomNumber 
   FROM sys.databases WHERE database_id <= 4) AS rando
INNER JOIN sys.databases d ON rando.RandomNumber = d.database_id

|--Compute Scalar(DEFINE:([Expr1071]=[Expr1070]))

|--Compute Scalar(DEFINE:([Expr1070]=(1)+abs(checksum(newid()))%(4)))

Vẫn đang tìm hiểu lý do tại sao nó chờ quá muộn để làm điều đó, nhưng hiện đang đọc bài đăng này của Paul White ( https://sql.kiwi/2012/09/compute-scalars-expressions-and-execut-plan-performance.html ) . Có lẽ nó có liên quan đến thực tế là NEWID không mang tính quyết định?


12

Điều này có thể cung cấp một số cái nhìn sâu sắc cho đến khi một trong những người thông minh hơn trên trang web chuông.

Tôi đặt kết quả ngẫu nhiên vào một bảng tạm thời và tôi luôn nhận được 4 kết quả bất kể loại tham gia.

/* Works as expected -- always four rows */

DECLARE @Rando table
(
    RandomNumber int
);

INSERT INTO
    @Rando
(
    RandomNumber
)
-- This generates 4 random numbers from 1 to 4, endpoints inclusive
SELECT
    1 + ABS(CHECKSUM(NEWID())) % (4) AS RandomNumber
FROM
    sys.databases
WHERE
    database_id <= 4;

SELECT
    *
FROM
    @Rando AS R;

SELECT
    rando.RandomNumber
,   d.database_id
FROM 
    @Rando AS rando
    LEFT JOIN 
        sys.databases d 
        ON rando.RandomNumber = d.database_id
ORDER BY 1,2;


/* Returns a varying number of rows */

SELECT rando.RandomNumber, d.database_id
FROM 
    @Rando AS rando
    INNER JOIN 
        sys.databases d 
        ON rando.RandomNumber = d.database_id
ORDER BY 1,2;

/* Also returns a varying number of rows */

WITH rando AS 
(
    SELECT * FROM @Rando AS rando
)
SELECT r.RandomNumber, d.database_id
FROM 
    rando AS r
    INNER JOIN 
        sys.databases d 
        ON r.RandomNumber = d.database_id
ORDER BY 1,2;

Nếu tôi so sánh các kế hoạch truy vấn giữa truy vấn thứ hai của bạn và biến thể với biến bảng, tôi có thể thấy có sự khác biệt nhất định giữa hai truy vấn. Chữ X màu đỏ No Join Predicatecó vẻ rất kỳ lạ đối với bộ não nhà phát triển thượng cổ của tôi

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

Nếu tôi loại bỏ một chút ngẫu nhiên của truy vấn thành một hằng số 1 % (4), kế hoạch của tôi có vẻ tốt hơn nhưng tính toán vô hướng đã bị loại bỏ khiến tôi phải nhìn gần hơn

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

Đó là tính toán biểu thức cho số ngẫu nhiên sau khi tham gia. Cho dù đó là dự kiến, tôi vẫn để lại cho các trình hướng dẫn nội bộ trên trang web nhưng ít nhất đó là lý do tại sao bạn nhận được kết quả khác nhau khi tham gia.

2014

Đối với những người chơi cùng ở nhà, các gói truy vấn trên được tạo từ phiên bản 2008 R2. Các kế hoạch năm 2014 trông khác nhau nhưng hoạt động tính toán vô hướng vẫn còn sau khi tham gia.

Đây là kế hoạch truy vấn cho năm 2014 bằng cách sử dụng biểu thức hằng

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

Đây là kế hoạch truy vấn cho một phiên bản 2014 bằng cách sử dụng biểu thức newid.

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

Đây rõ ràng là do thiết kế, kết nối vấn đề ở đây. Cảm ơn @paulWhite vì đã biết rằng đã tồn tại.


1
Đúng, chính xác - đó là những gì đang xảy ra, nhưng nó chắc chắn không được mong đợi. Các kết quả không khớp với T-SQL đang được truyền vào, và do đó, câu hỏi.
Brent Ozar

Ngay cả việc thay thế số ngẫu nhiên bằng số tĩnh 1 cũng mang lại cho toán tử tham gia không có biến vị ngữ nối
James Anderson

Có vẻ như bạn đang làm gì đó. Ngay cả khi sử dụng TÙY CHỌN (LỆNH FORCE) cũng không thay đổi hành vi - số ngẫu nhiên vẫn được tính sau cùng ...
Jeremiah Peschka

Loại bỏ sys.database TVF, sau đây tạo ra cùng một kế hoạch: gist.github.com/peschkaj/cebdeb98daa4d1f08dc5
Jeremiah Peschka

Điều này nghe có vẻ như là một vấn đề ưu tiên của nhà điều hành
James Anderson
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.