SQL kiri bergabung vs beberapa tabel pada baris FROM?

256

Sebagian besar dialek SQL menerima kedua pertanyaan berikut:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

Sekarang jelas ketika Anda membutuhkan gabungan luar, sintaks kedua diperlukan. Tetapi ketika melakukan inner join, mengapa saya lebih suka sintaks kedua daripada yang pertama (atau sebaliknya)?

jmucchiello
sumber
1
Guffa: Bagaimana Anda menemukan itu? Meskipun pertanyaan saya adalah praktik terbaik daripada "bagaimana saya"
jmucchiello
Karena ini adalah praktik terbaik, silakan buat ini sebagai Wiki.
Binoj Antony
1
Saya tidak berpikir ada yang berkomentar tentang kinerja keduanya. Adakah yang bisa mengkonfirmasi atau mengutip sesuatu yang masuk akal mengenai perbedaan yang signifikan?
ahnbizcad
@ ahnbizcad Dua pertanyaan yang diberikan tidak melakukan hal yang sama. Yang pertama mengembalikan sama dengan INNER JOIN ON. Implementasi adalah versi DBMS khusus, dan itupun memiliki beberapa jaminan. Tetapi transformasi DBMS menyamakan kasus koma vs INNER JOIN ON / WHERE vs CROSS JOIN WHERE itu sepele. Pelajari tentang optimasi / implementasi kueri basis data relasional.
philipxy
mendapat rekomendasi sumber daya? manual raksasa dan padat itulah sebabnya saya mencoba belajar dari sini.
ahnbizcad

Jawaban:

319

Sintaksis lama, dengan hanya mencantumkan tabel, dan menggunakan WHEREklausa untuk menentukan kriteria bergabung, sedang tidak digunakan di sebagian besar database modern.

Ini bukan hanya untuk pertunjukan, sintaks lama memiliki kemungkinan ambigu ketika Anda menggunakan INNER dan OUTER bergabung dalam kueri yang sama.

Biarkan saya memberi Anda sebuah contoh.

Misalkan Anda memiliki 3 tabel di sistem Anda:

Company
Department
Employee

Setiap tabel berisi banyak baris, dihubungkan bersama. Anda memiliki banyak perusahaan, dan setiap perusahaan dapat memiliki banyak departemen, dan setiap departemen dapat memiliki beberapa karyawan.

Oke, jadi sekarang Anda ingin melakukan hal berikut:

Daftar semua perusahaan, dan sertakan semua departemen mereka, dan semua karyawan mereka. Perhatikan bahwa beberapa perusahaan belum memiliki departemen, tetapi pastikan Anda memasukkannya juga. Pastikan Anda hanya mengambil departemen yang memiliki karyawan, tetapi selalu daftarkan semua perusahaan.

Jadi kamu melakukan ini:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

Perhatikan bahwa yang terakhir ada gabungan dalam, untuk memenuhi kriteria bahwa Anda hanya ingin departemen dengan orang.

Ok, jadi apa yang terjadi sekarang. Masalahnya adalah, itu tergantung pada mesin basis data, pengoptimal kueri, indeks, dan statistik tabel. Biarkan saya jelaskan.

Jika pengoptimal kueri menentukan bahwa cara untuk melakukan ini adalah pertama-tama mengambil sebuah perusahaan, kemudian menemukan departemen, dan kemudian bergabung dengan karyawan, Anda tidak akan mendapatkan perusahaan yang tidak memiliki departemen.

Alasan untuk ini adalah bahwa WHEREklausa menentukan baris mana berakhir pada hasil akhir, bukan bagian individu dari baris.

Dan dalam hal ini, karena bergabung kiri, kolom Department.ID akan NULL, dan dengan demikian ketika datang ke INNER BERGABUNG ke Karyawan, tidak ada cara untuk memenuhi kendala itu untuk baris Karyawan, dan sehingga tidak akan muncul.

Di sisi lain, jika pengoptimal kueri memutuskan untuk menangani bergabung dengan departemen-karyawan terlebih dahulu, dan kemudian lakukan bergabung dengan kiri dengan perusahaan, Anda akan melihatnya.

Jadi sintaksis lama bersifat ambigu. Tidak ada cara untuk menentukan apa yang Anda inginkan, tanpa berurusan dengan petunjuk kueri, dan beberapa database tidak memiliki cara sama sekali.

Masukkan sintaks baru, dengan ini Anda dapat memilih.

Misalnya, jika Anda ingin semua perusahaan, seperti yang dijelaskan deskripsi masalah, inilah yang akan Anda tulis:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

Di sini Anda menentukan bahwa Anda ingin bergabung dengan departemen-karyawan dilakukan sebagai satu bergabung, dan kemudian pergi bergabung dengan hasil itu dengan perusahaan.

Selain itu, katakanlah Anda hanya ingin departemen yang berisi huruf X atas namanya. Sekali lagi, dengan gaya lama bergabung, Anda berisiko kehilangan perusahaan juga, jika tidak memiliki departemen dengan X dalam namanya, tetapi dengan sintaks baru, Anda dapat melakukan ini:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

Klausa tambahan ini digunakan untuk bergabung, tetapi bukan filter untuk seluruh baris. Jadi baris tersebut mungkin muncul dengan informasi perusahaan, tetapi mungkin memiliki NULL di semua kolom departemen dan karyawan untuk baris itu, karena tidak ada departemen dengan X dalam namanya untuk perusahaan itu. Ini sulit dengan sintaks lama.

Inilah sebabnya mengapa, di antara vendor lain, Microsoft telah mencabut sintaks join luar yang lama, tetapi bukan sintaks join dalam yang lama, sejak SQL Server 2005 dan yang lebih tinggi. Satu-satunya cara untuk berbicara dengan database yang berjalan di Microsoft SQL Server 2005 atau 2008, menggunakan sintaks join gaya lama, adalah dengan mengatur database itu dalam mode kompatibilitas 8.0 (alias SQL Server 2000).

Selain itu, cara lama, dengan melemparkan banyak tabel ke optimizer kueri, dengan sekelompok klausa WHERE, mirip dengan mengatakan "di sini Anda, lakukan yang terbaik yang Anda bisa". Dengan sintaks baru, optimizer kueri memiliki lebih sedikit pekerjaan yang harus dilakukan untuk mengetahui bagian mana yang berjalan bersama.

Jadi begitulah.

LEFT and INNER JOIN adalah gelombang masa depan.

Lasse V. Karlsen
sumber
28
"Sudah ditinggalkan di sebagian besar database modern." --- hanya ingin tahu, yang mana?
zerkms
10
maafkan saya, saya tidak terbiasa dengan operator * =, apa fungsinya? Terima kasih!
ultrajohn
9
Bintang = dan = Bintang (kanan) dari kanan dan kiri bergabung, atau apakah itu kiri dan kanan? Sudah usang sejak lama, saya belum menggunakannya sejak SQL Server 6.
Tony Hopkinson
3
Koma tidak ditinggalkan. OUTER JOINSintaks yang tidak pernah standar *=/ =*/ *=*sudah ditinggalkan.
philipxy
1
Jawaban ini bahkan tidak menjawab pertanyaan, yang bukan tentang gabungan luar. Klaim yang dibuat tentang koma vs INNER JOIN ON, re optimization, salah.
philipxy
17

Sintaks GABUNG menjaga kondisi di dekat tabel yang mereka terapkan. Ini sangat berguna ketika Anda bergabung dengan sejumlah besar tabel.

Ngomong-ngomong, Anda bisa melakukan join luar dengan sintaks pertama juga:

WHERE a.x = b.x(+)

Atau

WHERE a.x *= b.x

Atau

WHERE a.x = b.x or a.x not in (select x from b)
Andomar
sumber
2
Sintaks * = tidak digunakan lagi dalam MS SQLServer dan karena alasan yang baik: Tidak hanya membuatnya lebih sulit untuk dibaca, tetapi juga tidak melakukan apa yang orang pikirkan dan tidak BUKAN sama dengan LEFT JOIN. Sintaks (+) tidak saya kenal; apa implementasi SQL melakukan itu?
Euro Micelli
2
Sintaks lainnya digunakan oleh Oracle, setidaknya.
Lasse V. Karlsen
4
Jangan pernah menggunakan sintaks SQL Server * =, itu TIDAK akan memberikan hasil yang konsisten karena kadang-kadang akan ditafsirkan sebagai gabungan silang, bukan gabungan kiri. Ini benar bahkan sejauh SQL Server 2000. Jika Anda memiliki kode menggunakan ini, Anda perlu memperbaikinya.
HLGEM
12

Cara pertama adalah standar yang lebih lama. Metode kedua diperkenalkan di SQL-92, http://en.wikipedia.org/wiki/SQL . Standar lengkap dapat dilihat di http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt .

Butuh bertahun-tahun sebelum perusahaan database mengadopsi standar SQL-92.

Jadi alasan mengapa metode kedua lebih disukai, itu adalah standar SQL menurut komite standar ANSI dan ISO.

Dwight T
sumber
,masih standar. onperlu diperkenalkan hanya untuk outer joinsatu kali subseleksi juga diperkenalkan.
philipxy
12

Pada dasarnya, ketika klausa FROM Anda mencantumkan tabel seperti:

SELECT * FROM
  tableA, tableB, tableC

hasilnya adalah produk silang dari semua baris dalam tabel A, B, C. Kemudian Anda menerapkan batasan WHERE tableA.id = tableB.a_idyang akan membuang sejumlah besar baris, lalu selanjutnya ... AND tableB.id = tableC.b_iddan Anda kemudian harus mendapatkan hanya baris yang benar-benar Anda minati di.

DBMS tahu bagaimana mengoptimalkan SQL ini sehingga perbedaan kinerja untuk menulis ini menggunakan BERGABUNG diabaikan (jika ada). Menggunakan notasi JOIN membuat pernyataan SQL lebih mudah dibaca (IMHO, tidak menggunakan gabungan mengubah pernyataan menjadi berantakan). Menggunakan produk silang, Anda harus memberikan kriteria bergabung dalam klausa WHERE, dan itulah masalahnya dengan notasi. Anda memenuhi klausa WHERE Anda dengan hal-hal seperti

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

yang hanya digunakan untuk membatasi produk silang. Klausa WHERE hanya boleh berisi BATASAN untuk resultset. Jika Anda mencampur tabel bergabung dengan kriteria dengan batasan resultset, Anda (dan orang lain) akan menemukan kueri Anda lebih sulit untuk dibaca. Anda harus menggunakan JOINs dan menjaga klausa FROM klausa FROM, dan klausa WHERE klausa WHERE.

Peter Perháč
sumber
10

Yang kedua lebih disukai karena jauh lebih kecil kemungkinannya untuk menghasilkan gabungan silang yang tidak disengaja dengan lupa memasukkan klausa mana. Gabung tanpa klausa akan gagal memeriksa sintaksis, gabung gaya lama tanpa klausa mana tidak akan gagal, ia akan melakukan join silang.

Selain itu ketika nanti Anda harus bergabung dengan kiri, akan membantu untuk pemeliharaan bahwa mereka semua berada dalam struktur yang sama. Dan sintaks yang lama telah kedaluwarsa sejak 1992, sudah saatnya untuk berhenti menggunakannya.

Ditambah lagi, saya telah menemukan bahwa banyak orang yang secara eksklusif menggunakan sintaks pertama tidak benar-benar memahami gabungan dan memahami gabungan sangat penting untuk mendapatkan hasil yang benar ketika mengajukan pertanyaan.

HLGEM
sumber
6

Saya pikir ada beberapa alasan bagus pada halaman ini untuk mengadopsi metode kedua - menggunakan JOINs eksplisit. Yang menentukan adalah bahwa ketika kriteria BERGABUNG dihapus dari klausa WHERE itu menjadi lebih mudah untuk melihat kriteria seleksi yang tersisa dalam klausa WHERE.

Dalam pernyataan SELECT yang sangat kompleks, pembaca akan lebih mudah memahami apa yang sedang terjadi.

Alan G
sumber
5

The SELECT * FROM table1, table2, ...sintaks ok untuk beberapa tabel, tetapi menjadi eksponensial ( belum tentu pernyataan matematis akurat ) lebih keras dan lebih sulit untuk dibaca sebagai jumlah meja meningkat.

Sintaks JOIN lebih sulit untuk ditulis (di awal), tetapi membuatnya eksplisit kriteria apa yang mempengaruhi tabel mana. Ini membuatnya lebih sulit untuk membuat kesalahan.

Juga, jika semua gabungan adalah INNER, maka kedua versi tersebut setara. Namun, saat Anda memiliki OUTER bergabung di mana saja dalam pernyataan, segala sesuatunya menjadi jauh lebih rumit dan itu sebenarnya menjamin bahwa apa yang Anda tulis tidak akan menanyakan apa yang Anda pikir Anda tulis.

Euro Micelli
sumber
2

Saat Anda membutuhkan gabung luar, sintaks kedua tidak selalu diperlukan:

Peramal:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (meskipun sudah usang dalam versi 2000) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x

Tetapi kembali ke pertanyaan Anda. Saya tidak tahu jawabannya, tetapi mungkin terkait dengan fakta bahwa bergabung lebih alami (setidaknya secara sintaksis) daripada menambahkan ekspresi ke klausa di mana Anda melakukan hal itu: bergabung .

Pablo Santa Cruz
sumber
SQL server telah mencabut sintaks kiri bergabung dan bahkan dalam SQL Server 2000 tidak akan secara konsisten memberikan hasil yang benar (kadang-kadang tidak bergabung silang, bukan bergabung kiri) dan tidak boleh digunakan dalam SQL Server.
HLGEM
@HLGEM: Terima kasih atas informasinya. Saya akan memperbarui posting saya untuk mencerminkan apa yang Anda katakan.
Pablo Santa Cruz
0

Saya mendengar banyak orang mengeluh yang pertama terlalu sulit untuk dipahami dan tidak jelas. Saya tidak melihat masalah dengan itu, tetapi setelah diskusi itu, saya menggunakan yang kedua bahkan pada INNER BERGABUNG untuk kejelasan.

kemiller2002
sumber
1
Saya dibesarkan dengan kebiasaan tidak menggunakan sintaks JOIN dan melakukannya dengan cara pertama. Saya harus mengakui saya masih terjebak dalam kebiasaan sering kali hanya karena saya pikir otak saya telah dikondisikan untuk mengikuti logika itu, wheras bergabung sintaks di kali dengan saya tampaknya sulit untuk berpikir.
TheTXI
3
Saya juga diajarkan seperti itu. Saya mengubah gaya pengkodean saya, karena orang akan melihatnya dan tidak mudah mengenali apa yang sedang terjadi. Karena tidak ada perbedaan logis dan saya tidak dapat menemukan alasan untuk memilih yang pertama daripada yang terakhir, saya merasa bahwa saya harus beradaptasi untuk membuat kode lebih jelas untuk membantu orang lain memahami apa yang saya tulis.
kemiller2002
0

Untuk basis data, mereka pada akhirnya sama saja. Namun, untuk Anda, Anda harus menggunakan sintaks kedua dalam beberapa situasi. Demi mengedit kueri yang akhirnya harus menggunakannya (mengetahui bahwa Anda memerlukan gabungan kiri di mana Anda memiliki gabungan langsung), dan untuk konsistensi, saya akan memodelkan hanya pada metode 2. Ini akan membuat kueri membaca lebih mudah.

Jeff Ferland
sumber
0

Nah kueri pertama dan kedua dapat menghasilkan hasil yang berbeda karena LEFT JOIN menyertakan semua rekaman dari tabel pertama, bahkan jika tidak ada catatan yang sesuai di tabel sebelah kanan.

Gavin H
sumber