Kondisi dalam GABUNG atau DIMANA

194

Apakah ada perbedaan (kinerja, praktik terbaik, dll ...) antara menempatkan suatu kondisi dalam klausa JOIN vs klausa WHERE?

Sebagai contoh...

-- Condition in JOIN
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND CUS.FirstName = 'John'

-- Condition in WHERE
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE CUS.FirstName = 'John'

Yang mana yang Anda sukai (dan mungkin mengapa)?

Steve Dignan
sumber
4
Apakah Anda menjalankan dua pertanyaan? Apakah Anda memeriksa rencana eksekusi yang dihasilkan oleh dua kueri? Apa yang kamu amati?
S.Lott
22
@ S.Lott, kueri ini hanya untuk keperluan contoh. Saya hanya ingin tahu "secara umum" yang merupakan metode yang disukai - jika ada.
Steve Dignan
1
@Steve Dignan: Anda harus membandingkan ini dengan data sampel dan melihat rencana kueri. Jawabannya akan sangat, sangat jelas. Dan - bonus - Anda akan memiliki sepotong kode yang dapat Anda gunakan kembali ketika situasi yang lebih kompleks muncul.
S.Lott
1
Saya pribadi akan memasukkan kondisi dalam klausa GABUNG jika kondisinya menggambarkan relasinya. Kondisi umum yang hanya memfilter set hasil akan pergi ke bagian WHERE. MisalnyaFROM Orders JOIN OrderParties ON Orders.Id = OrderParties.Order AND OrderParties.Type = 'Recipient' WHERE Orders.Status = 'Canceled'
Glutexo

Jawaban:

154

Aljabar relasional memungkinkan pertukaran predikat dalam WHEREklausa dan INNER JOIN, sehingga bahkan INNER JOINpertanyaan dengan WHEREklausa dapat membuat predikat disusun ulang oleh pengoptimal sehingga mereka mungkin sudah dikecualikan selama JOINproses.

Saya sarankan Anda menulis pertanyaan dengan cara yang paling mudah dibaca.

Kadang-kadang ini termasuk membuat yang INNER JOINrelatif "tidak lengkap" dan menempatkan beberapa kriteria dalam WHEREhanya untuk membuat daftar kriteria penyaringan lebih mudah dipelihara.

Misalnya, alih-alih:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
    AND c.State = 'NY'
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
    AND a.Status = 1

Menulis:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
WHERE c.State = 'NY'
    AND a.Status = 1

Tapi itu tergantung, tentu saja.

Cade Roux
sumber
7
Ini bukan hanya tentang permintaan atau keterbacaan yang bersih, ini tentang kinerja. menempatkan kondisi dalam gabungan meningkatkan kinerja untuk sejumlah besar data dengan tabel yang diindeks dengan benar.
Shahdat
1
Saya baru saja menjalankan laporan penjualan bulanan dengan menggabungkan 5-6 tabel pada beberapa juta catatan. Perf meningkat 30% - sql server 2012
Shahdat
2
@ Shahdat jika Anda mendapatkan perbedaan kinerja yang signifikan yang memindahkan kondisi filter Anda dari klausa mana ke bagian dalam yang Anda perlukan untuk memposting rencana eksekusi tersebut.
Cade Roux
4
@Cade Saya telah menyelidiki rencana eksekusi - kedua skenario menunjukkan biaya yang sama. Saya menjalankan query beberapa kali sepertinya sama-sama mengambil waktu. Sebelumnya, saya menjalankan kueri pada produksi dan mendapat perbedaan kinerja yang signifikan karena database digunakan oleh pengguna langsung. Maaf atas kebingungan itu.
Shahdat
4
Jawaban ini tepat untuk GABUNGAN INNER tetapi tidak untuk gabung kiri / kanan.
sotn
123

Untuk inner joins, saya belum benar-benar memperhatikan perbedaan (tetapi seperti semua penyetelan kinerja, Anda perlu memeriksa database Anda di bawah kondisi Anda).

Namun di mana Anda meletakkan kondisi itu membuat perbedaan besar jika Anda menggunakan gabungan kiri atau kanan. Sebagai contoh, pertimbangkan dua pertanyaan ini:

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderDate >'20090515'

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND ORD.OrderDate >'20090515'

Yang pertama hanya akan memberi Anda catatan-catatan yang memiliki pesanan tertanggal lebih dari 15 Mei 2009 sehingga mengubah bergabung kiri untuk bergabung dalam.

Yang kedua akan memberikan catatan-catatan itu ditambah pelanggan mana pun tanpa pesanan. Hasil yang ditetapkan sangat berbeda tergantung di mana Anda meletakkan kondisinya. (Pilih * hanya untuk tujuan saja, tentu saja Anda tidak boleh menggunakan ini dalam kode produksi.)

Pengecualian untuk ini adalah ketika Anda hanya ingin melihat catatan dalam satu tabel tetapi tidak yang lain. Kemudian Anda menggunakan klausa where untuk kondisi bukan join.

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderID is null
HLGEM
sumber
Terima kasih telah menjelaskan dengan contoh
Rennish Joseph
1
msgstr "dengan demikian mengonversi gabung kiri menjadi gabung dalam". Bagaimana? Bisakah Anda sedikit menjelaskan?
user1451111
@ user1451111 Pelajari apa yang dihasilkan LEFT / RIGHT JOIN: INNER JOIN rows plus baris tabel kiri / kanan yang tak tertandingi yang diperpanjang oleh NULLs. FULL JOIN mengembalikan INNER JOIN rows UNION SEMUA baris tabel kiri & kanan yang tak tertandingi diperpanjang oleh NULLs. Selalu tahu INNER BERGABUNG yang Anda inginkan sebagai bagian dari OUTER JOIN. A WHERE atau ON yang membutuhkan kolom NULL-extended mungkin bukan NULL setelah OUTER JOIN ON menghapus setiap baris yang diperpanjang oleh NULLs, yaitu hanya menyisakan INNER JOIN rows, yaitu "mengubah OUTER JOIN menjadi INNER JOIN".
philipxy
1
@ user1451111 atau, dalam istilah yang lebih sederhana: A left join Badalah setiap baris dari A bergabung ke setiap baris yang cocok dari B. Jika B tidak memiliki baris yang cocok, maka kolom A memiliki nilai tetapi setiap kolom dari B pada baris itu ditampilkan sebagai nilai NULL. Jika Anda telah menulis where B.somecolumn = ‘somevalue’maka Anda memiliki NULL (B.somecolumn) dibandingkan dengan 'somevalue'. Apa pun yang dibandingkan dengan NULL adalah salah, jadi semua baris Anda di mana tidak ada baris B yang cocok untuk baris A, dihilangkan, dan hasil yang Anda dapatkan adalah sama dengan yang akan diberikan INNER JOIN, maka sambungan luar telah menjadi bagian dalam
Caius Jard
ya saya sudah memeriksa hasilnya sama untuk: SELECT funds.id, prosp.id DARI prospek fundsbergabung dalam (prosp.id = funds.lead_id dan prosp.is_manual = 'tidak') dan SELECT funds.id, prospek.id DARI fundskiri bergabung dengan prospek pada (prosp.id = funds.lead_id) di mana prosp.is_manual = 'tidak'
Rohit Dhiman
25

Sebagian besar produk RDBMS akan mengoptimalkan kedua pertanyaan secara identik. Dalam "SQL Performance Tuning" oleh Peter Gulutzan dan Trudy Pelzer, mereka menguji beberapa merek RDBMS dan tidak menemukan perbedaan kinerja.

Saya lebih suka untuk menjaga kondisi gabungan terpisah dari kondisi batasan kueri.

Jika Anda menggunakan OUTER JOINkadang - kadang perlu untuk memasukkan kondisi dalam klausa gabungan.

Bill Karwin
sumber
1
Saya setuju dengan Anda bahwa secara sintaksis itu lebih bersih, dan saya harus tunduk pada pengetahuan Anda tentang buku itu dan reputasi Anda yang sangat tinggi, tetapi saya dapat memikirkan 4 pertanyaan dalam minggu terakhir dengan rencana pelaksanaan yang sangat berbeda, waktu CPU, dan pembacaan logis ketika Saya pindah ke tempat predikat bergabung.
marr75
2
Anda bertanya tentang praktik terbaik. Segera setelah Anda mulai menguji bagaimana implementasi RDBMS spesifik bekerja, orang lain telah memberikan saran yang benar: benchmark.
Bill Karwin
12

DIMANA akan menyaring setelah GABUNG telah terjadi.

Filter pada JOIN untuk mencegah baris ditambahkan selama proses JOIN.

TheTXI
sumber
10
Semantik, mereka dicegah selama proses GABUNGAN INNER, tetapi pengoptimal dapat mengatur ulang INNER GABUNG dan MANA predikat sesuka hati, sehingga pengoptimal bebas untuk mengecualikan mereka nanti jika diinginkan.
Cade Roux
1
Cade Roux: Benar. Sering kali apa yang Anda tulis dalam SQL bukan yang diberikan optimizer ketika semua dikatakan dan dilakukan. Saya akan kira kemudian bahwa ini akan menjadi benar dalam dunia semua-teori, sementara jawaban Anda adalah tentu saja lebih benar dalam dunia pengoptimalan query otomatis :)
TheTXI
Saya suka penjelasan tentang kondisi ini diON
Robert Rocha
3

Saya lebih suka GABUNG untuk bergabung dengan tabel penuh / Tampilan dan kemudian gunakan WHERE Untuk memperkenalkan predikat set yang dihasilkan.

Secara sintaksis terasa lebih bersih.

Johnno Nolan
sumber
2

Saya biasanya melihat peningkatan kinerja saat memfilter pada gabungan. Terutama jika Anda dapat bergabung di kolom yang diindeks untuk kedua tabel. Anda harus bisa mengurangi pembacaan logis dengan sebagian besar pertanyaan melakukan ini juga, yang, dalam lingkungan volume tinggi, indikator kinerja yang jauh lebih baik daripada waktu eksekusi.

Saya selalu sedikit geli ketika seseorang menunjukkan benchmark SQL mereka dan mereka telah mengeksekusi kedua versi sproc 50.000 kali pada tengah malam di server dev dan membandingkan waktu rata-rata.

marr75
sumber
0

Menempatkan kondisi dalam gabung tampaknya "semantik salah" bagi saya, karena bukan itu GABUNGAN "untuk". Tapi itu sangat kualitatif.

Masalah tambahan: jika Anda memutuskan untuk beralih dari gabung dalam ke, katakanlah, gabung kanan, memiliki kondisi di dalam GABUNGAN dapat menyebabkan hasil yang tidak terduga.

Yakub B
sumber
3
Kadang-kadang hasil ini agak "diharapkan" dan kadang-kadang bahkan "disengaja" (misalnya dengan gabungan luar, di mana kondisi WHERE memiliki semantik yang berbeda dari kondisi BERGABUNG).
Marcel Toth
0

Bergabung lebih cepat menurut saya ketika Anda memiliki meja yang lebih besar. Sebenarnya tidak banyak perbedaan meskipun terutama jika Anda berurusan dengan meja yang lebih kecil. Ketika saya pertama kali belajar tentang bergabung, saya diberitahu bahwa kondisi dalam bergabung adalah seperti di mana kondisi klausa dan bahwa saya bisa menggunakannya secara bergantian jika klausa mana yang spesifik tentang tabel mana untuk melakukan kondisi.

Eric
sumber
-4

Lebih baik menambahkan kondisi di Gabung. Kinerja lebih penting daripada keterbacaan. Untuk dataset besar, itu penting.

Jeeno Shibu
sumber
1
Apakah Anda memiliki semacam bukti, meneliti bagaimana penempatan predikat tersebut mempengaruhi kinerja?
Zso