Sintaks INNER JOIN bersarang di dalam OUTER JOIN vs hasil kueri

10

TLDR; Jika Anda melihat 2 rencana eksekusi, apakah ada jawaban mudah yang lebih baik? Saya sengaja TIDAK membuat indeks sehingga lebih mudah untuk melihat apa yang terjadi.

Menindaklanjuti pertanyaan saya sebelumnya di mana kami menemukan perbedaan kinerja permintaan antara gaya bergabung yang berbeda (mis. Bersarang vs tradisional), saya telah menyadari bahwa sintaks bersarang juga memodifikasi perilaku kueri. Pertimbangkan 2 pertanyaan berikut.

SELECT  a.*, m.*, n.*
FROM    dbo.Autos a
LEFT JOIN dbo.Models m
  JOIN dbo.Manufacturers n  -- <-- Nested INNER JOIN
  ON n.ManufacturerID = m.ManufacturerID
ON m.ModelID = a.ModelID

masukkan deskripsi gambar di sini

Ini tidak harus membuat manufaktur bergabung untuk memasukkan baris otomatis dengan ModelID yang TIDAK dalam tabel Model.

masukkan deskripsi gambar di sini

Dengan menggunakan sintaksis tradisional, kita harus mengubah gabungan menjadi Manufaktur menjadi gabungan luar, seperti begitu ... tetapi ini mengubah rencana kueri.

SELECT a.*, m.*, n.*
FROM dbo.Autos a
LEFT JOIN dbo.Models m
ON m.ModelID = a.ModelID
LEFT JOIN dbo.Manufacturers n -- <-- Now LEFT OUTER JOIN
ON n.ManufacturerID = m.ManufacturerID

masukkan deskripsi gambar di sini

JeffInCO
sumber

Jawaban:

12

Jika Anda melihat 2 rencana eksekusi, apakah ada jawaban mudah yang lebih baik? Saya sengaja TIDAK membuat indeks sehingga lebih mudah untuk melihat apa yang terjadi.

Rencana kedua memiliki perkiraan biaya yang lebih rendah, sehingga dalam arti terbatas itu 'lebih baik'.

Set data sangat kecil sehingga pengoptimal tidak menghabiskan banyak waktu mencari alternatif. Bentuk pertama kueri terjadi untuk menemukan paket menggunakan hash join dan spool tabel sejak awal. Perkiraan biaya rencana itu sangat rendah sehingga pengoptimal tidak repot mencari yang lebih baik.

Bentuk kedua dari kueri terjadi untuk menemukan rencana menggunakan hanya loop bersarang luar bergabung di awal proses pencarian, dan lagi pengoptimal memutuskan bahwa rencana itu cukup baik. Kebetulan rencana ini diperkirakan lebih murah.

Yang mengatakan (seperti yang disebutkan dalam komentar pertanyaan) dua pertanyaan tidak identik secara semantik. Ini mungkin tidak penting bagi Anda jika Anda dapat menjamin bahwa hasilnya akan selalu sama untuk semua kemungkinan keadaan di masa depan dari database Anda, tetapi pengoptimal tidak dapat membuat asumsi itu. Itu hanya pernah menghasilkan rencana yang dijamin untuk menghasilkan hasil yang sama yang ditentukan oleh SQL, dalam semua keadaan.

Saya telah menyadari bahwa sintaks bersarang juga memodifikasi perilaku kueri.

'Nested syntax' hanyalah salah satu aspek dari keseluruhan sintaksis gabungan ANSI. Untuk mengaktifkan spesifikasi logis lengkap untuk pola penyatuan yang lebih kompleks, spesifikasi tersebut memungkinkan tanda kurung (opsional), dan FROMsubqueries klausa.

Kueri dapat ditulis menggunakan sintaks ANSI yang sama menggunakan tanda kurung:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN
(
    dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) ON M.ModelID = A.ModelID;

Formulir ini jelas menunjukkan bahwa persyaratan logis adalah ke kiri bergabung dari Autoske hasil dari batin bergabung Manufacturerske Models. Menghilangkan tanda kurung opsional memberikan formulir yang Anda sebut 'bersarang':

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
    ON M.ModelID = A.ModelID;

Ini bukan sintaks yang berbeda - ini hanya menghilangkan tanda kurung opsional dan memformat ulang sedikit.

Seperti yang disebutkan Martin, mungkin juga dalam hal ini untuk menyatakan persyaratan logis menggunakan gabungan dalam diikuti oleh gabungan luar kanan:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
RIGHT JOIN dbo.Autos AS A
    ON A.ModelID = M.ModelID;

Ketiga formulir permintaan di atas menggunakan sintaks gabungan ANSI yang sama. Ketiganya juga menghasilkan rencana eksekusi fisik yang sama dengan kumpulan data yang disediakan:

Rencana eksekusi umum

Seperti yang saya sebutkan dalam jawaban saya untuk pertanyaan Anda sebelumnya, pertanyaan yang mengungkapkan persyaratan logis yang persis sama tidak akan selalu menghasilkan rencana eksekusi yang sama. Bentuk kueri logis mana yang Anda lebih suka gunakan sebagian besar adalah masalah gaya. Tidak ada korelasi antara satu gaya tertentu dan rencana permintaan 'lebih baik' secara umum. Saya biasanya menyarankan untuk tidak menulis ulang permintaan untuk mendapatkan rencana tertentu jika permintaan baru tidak benar-benar identik dengan aslinya.

Standar SQL juga memungkinkan FROMsubkueri klausa, jadi cara lain untuk menulis spesifikasi kueri yang sama adalah:

SELECT * 
FROM dbo.Autos AS A
LEFT JOIN
(
    SELECT
        N.ManufacturerID,
        ManufacturerName = N.Name,
        M.ModelID,
        ModelName = M.Name
    FROM dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) AS R1
    ON R1.ModelID = A.ModelID;

Menggunakan sintaksis tradisional, kita harus mengubah gabungan menjadi `Pabrikan menjadi gabungan luar, seperti begitu ... tetapi ini mengubah rencana kueri.

Ini mungkin mengubah arti dari query, dalam hal ini secara teknis tidak alternatif yang valid (tapi lihat ypercube 's komentar pada pertanyaan Anda).

Tanda kurung (opsional) dalam sintaks gabungan ANSI ada tepat untuk persyaratan bergabung yang lebih kompleks seperti ini, jadi Anda tidak perlu takut menggunakannya jika perlu.

Paul White 9
sumber