Rasgele sayılar ve birleştirme türleriyle beklenmeyen sonuçlar


16

Dört rasgele sayılar (1-4) alır ve sonra eşleşen database_id numarasını almak için geri katıldı basit bir komut dosyası var. Bir LEFT JOIN ile senaryoyu çalıştırdığımda, her seferinde dört satır alıyorum (beklenen sonuç). Ancak, bir INNER JOIN ile çalıştırdığımda, değişen sayıda satır alıyorum - bazen iki, bazen sekiz.

Mantıksal olarak, herhangi bir fark olmamalı çünkü sys.databases veritabanı_id 1-4 ile satır var biliyorum. Ve dört satırlı rastgele sayı tablosundan (ona katılmanın aksine) seçim yaptığımız için, asla dörtten fazla satır döndürülmemelidir.

Bu, hem SQL Server 2012 hem de 2014'te olur. INNER JOIN değişken sayıda satır döndürmek için ne neden olur?

/* 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
Her zaman 4 satır almanın başka bir yolu: 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()) ;Sanırım iyi çalışıyor çünkü deterministik olmayan fonksiyonun değerinde bir birleşim yok.
ypercubeᵀᴹ

Yanıtlar:


9

Ek SELECT ekleyerek hesaplama skaler değerlendirmesini planın derinliklerine iter ve birleştirme yüklemini verir, üstteki hesaplama skaleri daha sonra öncekine referans verir.

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

Hala neden bu kadar geç beklediğini araştırıyorum, ancak şu anda bu yazıyı Paul White tarafından okuyor ( https://sql.kiwi/2012/09/compute-scalars-expressions-and-execution-plan-performance.html ) . Belki de NEWID'in deterministik olmamasıyla bir ilgisi var mı?


12

Bu, sitedeki daha akıllı kişilerden biri çalıncaya kadar biraz fikir verebilir.

Rastgele sonuçları geçici bir tabloya koydum ve birleştirme türünden bağımsız olarak sürekli olarak 4 sonuç alıyorum.

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

İkinci sorgunuz ve varyasyon ile bir tablo değişkeni arasındaki sorgu planlarını karşılaştırırsam, ikisi arasında kesin bir fark olduğunu görebilirim. Kırmızı X, No Join Predicatemağara adamı geliştirici beynime gerçekten tuhaf görünüyor

resim açıklamasını buraya girin

Sorgunun rastgele bitini bir sabite kaldırırsam, 1 % (4)planım daha iyi görünüyor, ancak Hesaplama Skalerleri ortadan kaldırıldı, böylece daha yakından görünmeme yol açtı

resim açıklamasını buraya girin

Birleştirmeden sonra rastgele sayının ifadesini hesaplıyor. Beklendiği gibi, yine de sitedeki dahili sihirbazlara bırakıyorum, ancak en azından bu yüzden katılımınızda değişken sonuçlar alıyorsunuz.

2014

Evde oynayanlar için yukarıdaki sorgu planları 2008 R2 örneğinden oluşturuldu. 2014 planları farklı görünüyor ancak Hesaplama Skaler işlemi birleştirme işleminden sonra da devam ediyor.

Bu, sabit ifadeyi kullanan bir 2014 için sorgu planıdır

resim açıklamasını buraya girin

Bu, newid ifadesini kullanan bir 2014 örneği için sorgu planıdır.

resim açıklamasını buraya girin

Bu görünüşte tasarım, Connect sorunu burada. @PaulWhite'a var olduğunu bildiği için teşekkürler.


1
Doğru, tam olarak - olan bu, ama kesinlikle beklenmiyor. Sonuçlar, iletilen T-SQL ve dolayısıyla soru ile eşleşmiyor.
Brent Ozar

Rastgele sayıyı statik 1 ile değiştirmek bile birleştirme operatörüne bir birleşim yüklemesi vermez
James Anderson

Görünüşe göre bir şey yapıyorsun. OPTION (FORCE ORDER) kullanmak bile davranışı değiştirmez - rastgele sayı hala son hesaplanır ...
Jeremiah Peschka


Bu bir operatör önceliği sorunu gibi görünüyor
James Anderson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.