Haruskah saya membuat sarang luar yang tergantung di SQL Server?

9

Saya telah mendengar informasi yang beragam tentang hal ini dan saya mengharapkan pendapat kanonik atau ahli.

Jika saya memiliki banyak LEFT OUTER JOINs, masing-masing bergantung pada yang terakhir, apakah lebih baik untuk membuat sarang?

Untuk contoh yang dibuat-buat, JOINto MyParenttergantung pada JOINke MyChild: http://sqlfiddle.com/#!3/31022/5

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    MyChild AS c
        ON c.[Id] = gc.[ParentId]
LEFT OUTER JOIN
    MyParent AS p
        ON p.[id] = c.[ParentId]

masukkan deskripsi gambar di sini

Dibandingkan dengan http://sqlfiddle.com/#!3/31022/7

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    (
    MyChild AS c            
    LEFT OUTER JOIN
        MyParent AS p
            ON p.[id] = c.[ParentId]
    )
    ON c.[Id] = gc.[ParentId]

masukkan deskripsi gambar di sini

Seperti yang ditunjukkan di atas ini menghasilkan rencana permintaan yang berbeda di SS2k8

Matius
sumber
Saya suka menggunakan nested joins: michaeljswart.com/2012/09/when-i-use-nested-joins Mungkin masalah gaya.
Michael J Swart
@MichaelJSwart blog Anda hanya muncul untuk membahas ketika tanggungannya JOINadalahINNER JOIN
Matius
1
Bagaimana Anda ingin mendefinisikan "lebih baik"? Secara pribadi, saya menemukan yang pertama jauh lebih mudah dibaca - pikiran saya tidak terpental mencoba membalikkan hubungan. Memiliki ON ... ONdua kali berturut-turut (tanda kurung atau tidak) sangat membingungkan.
Aaron Bertrand
4
Ketika saya tidak menemukan perbedaan kinerja antara dua cara melakukan sesuatu, pertanyaan berikutnya yang saya tanyakan pada diri saya adalah: jika saya ditabrak bus atau memenangkan lotre malam ini, versi mana yang paling mudah dipahami dan dipelihara oleh siapa pun yang mengambil alih kode saya besok ?
Aaron Bertrand
1
The use planhint bekerja ketika tanam rencana permintaan kedua untuk pertama tetapi tidak sebaliknya.
Martin Smith

Jawaban:

3

Ini sama sekali bukan jawaban kanonik tapi saya perhatikan bahwa untuk rencana kueri nested loop yang ditunjukkan dalam SQL Fiddle dimungkinkan untuk menerapkan rencana dari Query 2 ke Query 1 dengan menggunakan USE PLANpetunjuk tetapi mencoba operasi sebaliknya gagal dengan

Prosesor kueri tidak dapat menghasilkan rencana kueri karena petunjuk USE PLAN berisi rencana yang tidak dapat diverifikasi sebagai sah untuk kueri. Hapus atau ganti petunjuk USE PLAN. Untuk kemungkinan terbaik dari memaksa rencana berhasil, verifikasi bahwa rencana yang disediakan dalam petunjuk USE PLAN adalah yang dihasilkan secara otomatis oleh SQL Server untuk permintaan yang sama.

Menonaktifkan aturan transformasi pengoptimal ReorderLOJN mencegah petunjuk rencana sukses sebelumnya juga berhasil.

Bereksperimen dengan jumlah yang lebih besar dari acara data yang SQL Server tentu mampu mengubah (A LOJ B) LOJ Cke A LOJ (B LOJ C)alami juga tapi aku tidak melihat bukti bahwa sebaliknya adalah benar.

Kasus yang sangat dibuat-buat di mana kueri pertama berkinerja lebih baik daripada yang kedua

DROP TABLE  MyGrandChild , MyChild,  MyParent

CREATE TABLE MyParent
(Id int)

CREATE TABLE MyChild
(Id int PRIMARY KEY
,ParentId int,
Filler char(8000) NULL)

CREATE TABLE MyGrandChild
(Id int
,ParentId int)

INSERT INTO MyChild
                      (Id, ParentId)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY @@SPID),
                     ROW_NUMBER() OVER (ORDER BY @@SPID)    
FROM master..spt_values  v1, master..spt_values                  

INSERT INTO MyGrandChild
                      (Id, ParentId)
OUTPUT INSERTED.Id INTO MyParent
SELECT TOP (3000) Id, Id AS ParentId
FROM MyChild
ORDER BY Id

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN MyChild AS c
         ON c.[Id] = gc.[ParentId]
       LEFT OUTER JOIN MyParent AS p
         ON p.[Id] = c.[ParentId]

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN( MyChild AS c
                        LEFT OUTER JOIN MyParent AS p
                          ON p.[Id] = c.[ParentId])
         ON c.[Id] = gc.[ParentId] 

Yang memberi rencana

masukkan deskripsi gambar di sini

Bagi saya, Kueri 1 memiliki waktu yang telah berlalu 108 ms vs 1.163 ms untuk Kueri 2.

Pertanyaan 1

Table 'Worktable'. Scan count 0, logical reads 0 
Table 'MyChild'. Scan count 0, logical reads 9196
Table 'MyGrandChild'. Scan count 1, logical reads 7
Table 'MyParent'. Scan count 1, logical reads 5

Pertanyaan 2

Table 'MyParent'. Scan count 1, logical reads 15000
Table 'MyChild'. Scan count 0, logical reads 9000 
Table 'MyGrandChild'. Scan count 1, logical reads 7

Jadi mungkin untuk sementara diasumsikan bahwa sintaks pertama ("tidak dicoba") berpotensi menguntungkan karena memungkinkan lebih banyak pesanan bergabung yang potensial untuk dipertimbangkan, tetapi saya belum melakukan pengujian yang cukup mendalam untuk memiliki banyak kepercayaan dalam hal ini sebagai aturan umum.

Sangat mungkin untuk memberikan contoh tandingan di mana Kueri 2 berkinerja lebih baik. Coba keduanya dan lihat rencana eksekusi.

Martin Smith
sumber
-1

tidak ada Jenis GABUNGAN yang disebut "Nested Join". itu adalah variasi lain dari penulisan yang GABUNG mungkin untuk tujuan kenyamanan mudah dibaca. Anda dapat melihatnya sebagai "Sub Pertanyaan" hanya untuk memahami tujuan.

jika Anda lebih peduli tentang keterbacaan kode maka menurut saya, itu adalah pilihan individu apa yang dapat mereka selesaikan.

Dan jika Anda khawatir dengan kinerja kueri, dan petunjuk "Force JOIN ORDER" tidak digunakan dalam kueri, maka tidak masalah jika kueri ditulis dengan "Nested Join" atau All "Outer Join". SQL server datang dengan urutan berdasarkan biaya bergabung dengan dua tabel dan / atau hasil. SQL Server BERGABUNG di antara dua set data sekaligus.

sebenarnya, bayangkan bahwa dengan cara kedua "bersarang bergabung" jika SQL server memutuskan untuk melakukan bagian kedua, "MyChild AS c LEFT OUTER BERGABUNG MyParent AS p ON p. [id] = c. [ParentId]" dan tabel tersebut terjadi untuk memiliki baris yang akan dibuang di BERGABUNG LEFT BERIKUTNYA. dalam hal ini SQL server telah menghabiskan sumber daya yang tidak perlu untuk melakukan OUTER GABUNG keduanya dan meneruskan hasilnya ke GABUNG berikutnya.

Anda juga dapat melihat pertanyaan serupa yang ditanyakan dan dijawab dengan tepat di sini. Memahami sintaks 'Nested join'

Anup Shah
sumber
1
Mengapa, kemudian, apakah mereka menghasilkan rencana kueri yang berbeda tanpa menggunakan FORCE JOIN ORDERpetunjuk?
Matius
tanpa petunjuk itu, kami tidak dapat menjamin pesanan GABUNG dan karena Anda melihat berbagai rencana eksekusi yang membuktikan hal itu. misalnya dalam cara pertama "menggunakan semua Gabung Luar" SQL server mungkin melakukan salah satu dari Dua ini. pertama "MyChild + MyGrandChild" dan Kemudian BERGABUNG ke "MyParent". Atau Pertama "MyChild + MyParent" dan kemudian BERGABUNG ke "MyGrandChild".
Anup Shah