Hasil yang tidak terduga dengan angka acak dan tipe gabungan


16

Saya memiliki skrip sederhana yang mendapat empat angka acak (1 hingga 4) dan kemudian bergabung kembali untuk mendapatkan nomor database_id yang cocok. Ketika saya menjalankan skrip dengan BERGABUNG KIRI, saya mendapatkan empat baris kembali setiap kali (hasil yang diharapkan). Namun, ketika saya menjalankannya dengan INNER JOIN, saya mendapatkan jumlah baris yang bervariasi - terkadang dua, kadang delapan.

Secara logis, seharusnya tidak ada perbedaan karena saya tahu baris dengan database_ids 1-4 ada di sys.databases. Dan karena kita memilih dari tabel angka acak dengan empat baris (sebagai lawan bergabung dengannya), seharusnya tidak pernah ada lebih dari empat baris yang dikembalikan.

Ini terjadi di SQL Server 2012 dan 2014. Apa yang menyebabkan INNER JOIN untuk mengembalikan jumlah baris yang berbeda?

/* 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
Cara lain untuk selalu mendapatkan 4 baris: 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()) ;Saya kira itu berfungsi dengan baik karena tidak ada gabungan pada nilai fungsi non-deterministik.
ypercubeᵀᴹ

Jawaban:


9

Dengan menambahkan SELECT tambahan itu mendorong evaluasi skalar komputasi lebih dalam ke dalam rencana dan memberikan predikat gabungan, skalar komputasi di atas kemudian referensi yang sebelumnya.

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)))

Masih menggali mengapa terlambat menunggu untuk melakukannya, tetapi saat ini membaca posting ini oleh Paul White ( https://sql.kiwi/2012/09/compute-scalars-expressions-and-execution-plan-performance.html ) . Mungkin itu ada hubungannya dengan fakta bahwa NEWID tidak deterministik?


12

Ini mungkin memberikan beberapa wawasan sampai salah satu dari orang-orang yang lebih pintar di situs tersebut berdebat.

Saya menempatkan hasil acak ke dalam tabel sementara dan saya secara konsisten mendapatkan 4 hasil terlepas dari tipe gabungan.

/* 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;

Jika saya membandingkan paket kueri antara kueri kedua dan variasinya dengan variabel tabel, saya bisa melihat ada perbedaan yang pasti antara keduanya. X merah No Join Predicatebegitu sepertinya aneh bagi otak pengembang gua saya

masukkan deskripsi gambar di sini

Jika saya menghilangkan bit acak dari kueri ke konstanta 1 % (4), rencana saya terlihat lebih baik tetapi Compute Scalar dihilangkan sehingga membuat saya melihat lebih dekat

masukkan deskripsi gambar di sini

Ini menghitung ekspresi untuk angka acak setelah bergabung. Apakah itu yang diharapkan, saya masih tetap pergi ke penyihir internal di situs tetapi setidaknya itu sebabnya Anda mendapatkan hasil yang bervariasi dalam bergabung Anda.

2014

Bagi mereka yang bermain bersama di rumah, rencana kueri di atas dihasilkan dari instance 2008 R2. Paket 2014 terlihat berbeda tetapi operasi Hitung Skalar tetap setelah bergabung.

Ini adalah paket kueri untuk 2014 menggunakan ekspresi konstan

masukkan deskripsi gambar di sini

Ini adalah paket kueri untuk instance 2014 yang menggunakan ekspresi newid.

masukkan deskripsi gambar di sini

Ini tampaknya adalah dengan desain, masalah Connect di sini. Terima kasih kepada @paulWhite karena mengetahui hal itu ada.


1
Benar, persis - itulah yang terjadi, tapi itu pasti tidak diharapkan. Hasilnya tidak cocok dengan T-SQL yang diteruskan, dan dengan demikian pertanyaannya.
Brent Ozar

Bahkan mengganti angka acak dengan 1 statis memberi operator bergabung dengan predikat tidak bergabung
James Anderson

Sepertinya Anda sedang melakukan sesuatu. Bahkan menggunakan OPTION (FORCE ORDER) tidak mengubah perilaku - angka acak masih dihitung terakhir ...
Jeremiah Peschka

Menghapus sys.databases TVF, berikut ini menghasilkan rencana yang sama: gist.github.com/peschkaj/cebdeb98daa4d1f08dc5
Jeremiah Peschka

Ini kedengarannya seperti masalah prioritas operator
James Anderson
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.