Saya ingin melakukan Full Outer Join di MySQL. Apakah ini mungkin? Apakah Full Outer Join didukung oleh MySQL?
Saya ingin melakukan Full Outer Join di MySQL. Apakah ini mungkin? Apakah Full Outer Join didukung oleh MySQL?
Jawaban:
Anda tidak memiliki GABUNGAN LENGKAP pada MySQL, tetapi Anda pasti bisa meniru mereka .
Untuk kode SAMPLE yang ditranskripsi dari pertanyaan SO ini, Anda memiliki:
dengan dua tabel t1, t2:
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
Kueri di atas berfungsi untuk kasus khusus di mana operasi FULL OUTER JOIN tidak akan menghasilkan baris duplikat. Kueri di atas tergantung pada UNION
operator yang ditetapkan untuk menghapus baris duplikat yang diperkenalkan oleh pola kueri. Kita bisa menghindari memperkenalkan baris duplikat dengan menggunakan pola anti-gabung untuk kueri kedua, dan kemudian menggunakan operator set UNION ALL untuk menggabungkan dua set. Dalam kasus yang lebih umum, di mana FULL OUTER JOIN akan mengembalikan baris duplikat, kita bisa melakukan ini:
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION ALL
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.id IS NULL
(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
t1
dan t2
, kueri dalam jawaban ini mengembalikan resultset yang mengemulasi FULL OUTER JOIN. Tetapi dalam kasus yang lebih umum, misalnya, daftar SELECT tidak mengandung kolom / ekspresi yang cukup untuk membuat baris yang dikembalikan unik, maka pola kueri ini tidak cukup untuk mereproduksi set yang akan diproduksi oleh a FULL OUTER JOIN
. Untuk mendapatkan emulasi yang lebih setia, kita membutuhkan UNION ALL
operator yang ditetapkan, dan salah satu pertanyaan membutuhkan pola anti-gabung . Komentar dari Pavle Lekic (di atas) memberikan pola kueri yang benar .
Jawaban yang diberikan Pablo Santa Cruz benar; namun, jika ada yang menemukan halaman ini dan ingin klarifikasi lebih lanjut, ini adalah rinciannya.
Misalkan kita memiliki tabel berikut:
-- t1
id name
1 Tim
2 Marta
-- t2
id name
1 Tim
3 Katarina
Gabungan dalam, seperti ini:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
Akan membuat kami hanya catatan yang muncul di kedua tabel, seperti ini:
1 Tim 1 Tim
Gabungan dalam tidak memiliki arah (seperti kiri atau kanan) karena keduanya dua arah secara eksplisit - kami membutuhkan kecocokan di kedua sisi.
Gabungan luar, di sisi lain, adalah untuk menemukan catatan yang mungkin tidak memiliki kecocokan di tabel lain. Dengan demikian, Anda harus menentukan sisi gabung mana yang diizinkan memiliki catatan yang hilang.
LEFT JOIN
dan RIGHT JOIN
merupakan singkatan untuk LEFT OUTER JOIN
dan RIGHT OUTER JOIN
; Saya akan menggunakan nama lengkap mereka di bawah ini untuk memperkuat konsep joins luar vs joins dalam.
Gabung luar kiri, seperti ini:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... akan memberi kita semua catatan dari tabel kiri terlepas dari apakah mereka memiliki kecocokan di tabel kanan, seperti ini:
1 Tim 1 Tim
2 Marta NULL NULL
Gabung luar kanan, seperti ini:
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... akan memberi kita semua catatan dari tabel kanan terlepas dari apakah mereka memiliki kecocokan di tabel kiri, seperti ini:
1 Tim 1 Tim
NULL NULL 3 Katarina
Gabungan luar penuh akan memberi kita semua catatan dari kedua tabel, terlepas dari apakah mereka memiliki kecocokan di tabel lain, dengan NULL di kedua sisi di mana tidak ada kecocokan. Hasilnya akan terlihat seperti ini:
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
Namun, seperti yang ditunjukkan Pablo Santa Cruz, MySQL tidak mendukung ini. Kita dapat menirunya dengan melakukan UNION dari join kiri dan join kanan, seperti ini:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
UNION
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
Anda dapat menganggap UNION
sebagai "menjalankan kedua pertanyaan ini, lalu menumpuk hasilnya di atas satu sama lain"; beberapa baris akan berasal dari permintaan pertama dan beberapa dari yang kedua.
Perlu dicatat bahwa a UNION
di MySQL akan menghilangkan duplikat yang tepat: Tim akan muncul di kedua pertanyaan di sini, tetapi hasil dari UNION
satu - satunya daftar dia sekali. Rekan guru basis data saya merasa bahwa perilaku ini tidak boleh diandalkan. Jadi untuk lebih eksplisit tentang itu, kita bisa menambahkan WHERE
klausa ke kueri kedua:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
UNION
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
WHERE `t1`.`id` IS NULL;
Di sisi lain, jika Anda ingin melihat duplikat karena suatu alasan, Anda bisa menggunakannya UNION ALL
.
FULL OUTER JOIN
. Tidak ada yang salah dengan melakukan kueri seperti itu, dan menggunakan UNION untuk menghapus duplikat itu. Tetapi untuk benar-benar meniru FULL OUTER JOIN
, kita perlu salah satu pertanyaan untuk menjadi anti-gabung.
UNION
operasi akan menghapus duplikat tersebut; tetapi itu juga menghapus SEMUA baris duplikat, termasuk baris duplikat yang akan dikembalikan oleh FULL OUTER JOIN. Untuk meniru a FULL JOIN b
, pola yang benar adalah (a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
.
Menggunakan union
kueri akan menghapus duplikat, dan ini berbeda dari perilaku full outer join
yang tidak pernah menghapus duplikat apa pun:
[Table: t1] [Table: t2]
value value
------- -------
1 1
2 2
4 2
4 5
Ini adalah hasil yang diharapkan dari full outer join
:
value | value
------+-------
1 | 1
2 | 2
2 | 2
Null | 5
4 | Null
4 | Null
Ini adalah hasil dari menggunakan left
dan right Join
dengan union
:
value | value
------+-------
Null | 5
1 | 1
2 | 2
4 | Null
Permintaan saya yang disarankan adalah:
select
t1.value, t2.value
from t1
left outer join t2
on t1.value = t2.value
union all -- Using `union all` instead of `union`
select
t1.value, t2.value
from t2
left outer join t1
on t1.value = t2.value
where
t1.value IS NULL
Hasil kueri di atas sama dengan hasil yang diharapkan:
value | value
------+-------
1 | 1
2 | 2
2 | 2
4 | NULL
4 | NULL
NULL | 5
@Steve Chambers : [Dari komentar, dengan banyak terima kasih!]
Catatan: Ini mungkin solusi terbaik, baik untuk efisiensi dan untuk menghasilkan hasil yang sama seperti aFULL OUTER JOIN
. Posting blog ini juga menjelaskannya dengan baik - mengutip dari Metode 2: "Ini menangani duplikat baris dengan benar dan tidak termasuk apa pun yang tidak seharusnya. Ini perlu untuk digunakanUNION ALL
alih-alih polosUNION
, yang akan menghilangkan duplikat yang ingin saya pertahankan. Ini mungkin jauh lebih efisien pada set hasil yang besar, karena tidak perlu menyortir dan menghapus duplikat. "
Saya memutuskan untuk menambahkan solusi lain yang datang dari full outer join
visualisasi dan matematika, tidak lebih baik dari yang di atas tetapi lebih mudah dibaca:
Gabung luar penuh berarti
(t1 ∪ t2)
: semua di dalamt1
atau dit2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only
: semua dalam keduanyat1
dant2
ditambah semuat1
yang tidak ada di dalamt2
dan ditambah semuat2
yang tidak ada dit1
:
-- (t1 ∩ t2): all in both t1 and t2
select t1.value, t2.value
from t1 join t2 on t1.value = t2.value
union all -- And plus
-- all in t1 that not exists in t2
select t1.value, null
from t1
where not exists( select 1 from t2 where t2.value = t1.value)
union all -- and plus
-- all in t2 that not exists in t1
select null, t2.value
from t2
where not exists( select 1 from t1 where t2.value = t1.value)
FULL OUTER JOIN
. Posting blog ini juga menjelaskannya dengan baik - mengutip dari Metode 2: "Ini menangani duplikat baris dengan benar dan tidak termasuk apa pun yang tidak seharusnya. Ini perlu untuk menggunakan UNION ALL alih-alih UNION polos, yang akan menghilangkan duplikat yang saya ingin tetap. Ini mungkin jauh lebih efisien pada set hasil yang besar, karena tidak perlu menyortir dan menghapus duplikat. "
MySql tidak memiliki sintaks FULL-OUTER-JOIN. Anda harus mencontoh dengan melakukan LEFT JOIN dan RIGHT JOIN sebagai berikut-
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
Tetapi MySql juga tidak memiliki sintaks KANAN BERGABUNG. Menurut penyederhanaan gabung luar MySql, gabung kanan dikonversi ke gabung kiri setara dengan mengganti t1 dan t2 di dalam FROM
dan ON
klausa dalam kueri. Dengan demikian, Pengoptimal Kueri MySql menerjemahkan kueri asli menjadi yang berikut -
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
Sekarang, tidak ada salahnya menulis kueri asli apa adanya, tetapi katakan jika Anda memiliki predikat seperti klausa WHERE, yang merupakan predikat sebelum bergabung atau predikat DAN pada ON
klausa, yang merupakan predikat saat bergabung , maka Anda mungkin ingin melihat iblis; yang secara rinci.
Pengoptimal permintaan MySql secara rutin memeriksa predikat jika mereka ditolak nol . Sekarang, jika Anda telah melakukan RIGHT JOIN, tetapi dengan predikat WHERE pada kolom dari t1, maka Anda mungkin berisiko mengalami skenario yang ditolak-nol .
Misalnya, kueri berikut ini -
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
diterjemahkan ke berikut ini oleh Pengoptimal Permintaan-
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
WHERE t1.col1 = 'someValue'
Jadi urutan tabel telah berubah, tetapi predikatnya masih diterapkan ke t1, tetapi t1 sekarang berada di klausa 'ON'. Jika t1.col1 didefinisikan sebagai NOT NULL
kolom, maka kueri ini akan ditolak-nol .
Gabung luar apa pun (kiri, kanan, penuh) yang ditolak-nol dikonversi menjadi gabung-dalam oleh MySql.
Dengan demikian hasil yang Anda harapkan mungkin sama sekali berbeda dari apa yang dikembalikan MySql. Anda mungkin berpikir itu bug dengan MySql's RIGHT JOIN, tapi itu tidak benar. Ini hanya cara kerja pengoptimal permintaan MySql. Jadi pengembang yang bertanggung jawab harus memperhatikan nuansa ini ketika dia membangun kueri.
Dalam SQLite Anda harus melakukan ini:
SELECT *
FROM leftTable lt
LEFT JOIN rightTable rt ON lt.id = rt.lrid
UNION
SELECT lt.*, rl.* -- To match column set
FROM rightTable rt
LEFT JOIN leftTable lt ON lt.id = rt.lrid
Tidak ada jawaban di atas yang benar, karena mereka tidak mengikuti semantik ketika ada nilai duplikat.
Untuk kueri seperti (dari duplikat ini ):
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.Name = t2.Name;
Setara yang benar adalah:
SELECT t1.*, t2.*
FROM (SELECT name FROM t1 UNION -- This is intentionally UNION to remove duplicates
SELECT name FROM t2
) n LEFT JOIN
t1
ON t1.name = n.name LEFT JOIN
t2
ON t2.name = n.name;
Jika Anda membutuhkan ini untuk bekerja dengan NULL
nilai - nilai (yang mungkin juga diperlukan), maka gunakan NULL
operator perbandingan -safe, <=>
bukan =
.
FULL OUTER JOIN
kapan pun name
kolomnya nol. The union all
query dengan pola anti-bergabung harus mereproduksi luar bergabung perilaku benar, tetapi solusi yang lebih tepat tergantung pada konteks dan kendala yang aktif pada tabel.
union all
, tetapi jawaban itu melewatkan pola anti-gabung baik dalam kueri pertama atau kedua yang akan membuat duplikat yang ada tetapi mencegah menambahkan yang baru. Tergantung pada konteksnya solusi lain (seperti ini) mungkin lebih tepat.
Permintaan shA.t yang dimodifikasi untuk kejelasan lebih lanjut:
-- t1 left join t2
SELECT t1.value, t2.value
FROM t1 LEFT JOIN t2 ON t1.value = t2.value
UNION ALL -- include duplicates
-- t1 right exclude join t2 (records found only in t2)
SELECT t1.value, t2.value
FROM t1 RIGHT JOIN t2 ON t1.value = t2.value
WHERE t2.value IS NULL
apa yang Anda katakan tentang solusi Cross join ?
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;
select (select count(*) from t1) * (select count(*) from t2))
baris di himpunan hasil.
SELECT
a.name,
b.title
FROM
author AS a
LEFT JOIN
book AS b
ON a.id = b.author_id
UNION
SELECT
a.name,
b.title
FROM
author AS a
RIGHT JOIN
book AS b
ON a.id = b.author_id
Itu juga mungkin, tetapi Anda harus menyebutkan nama bidang yang sama di pilih.
SELECT t1.name, t2.name FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT t1.name, t2.name FROM t2
LEFT JOIN t1 ON t1.id = t2.id
Saya memperbaiki respons, dan pekerjaan mencakup semua baris (berdasarkan respons Pavle Lekic)
(
SELECT a.* FROM tablea a
LEFT JOIN tableb b ON a.`key` = b.key
WHERE b.`key` is null
)
UNION ALL
(
SELECT a.* FROM tablea a
LEFT JOIN tableb b ON a.`key` = b.key
where a.`key` = b.`key`
)
UNION ALL
(
SELECT b.* FROM tablea a
right JOIN tableb b ON b.`key` = a.key
WHERE a.`key` is null
);
tablea
yang tidak memiliki kecocokan di tableb
dan sebaliknya. Anda mencoba UNION ALL
, yang hanya akan berfungsi jika dua tabel ini memiliki kolom pesanan yang setara, yang tidak dijamin.
Menjawab:
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;
Dapat diciptakan kembali sebagai berikut:
SELECT t1.*, t2.*
FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp
LEFT JOIN t1 ON t1.id = tmp.id
LEFT JOIN t2 ON t2.id = tmp.id;
Menggunakan jawaban UNION atau UNION ALL tidak mencakup kasus tepi di mana tabel dasar memiliki entri yang digandakan.
Penjelasan:
Ada kasus tepi yang UNION atau UNION ALL tidak bisa tutupi. Kami tidak dapat menguji ini di mysql karena tidak mendukung FULL OUTER JOINs, tetapi kami dapat mengilustrasikannya pada database yang mendukungnya:
WITH cte_t1 AS
(
SELECT 1 AS id1
UNION ALL SELECT 2
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 6
),
cte_t2 AS
(
SELECT 3 AS id2
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 6
)
SELECT * FROM cte_t1 t1 FULL OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2;
This gives us this answer:
id1 id2
1 NULL
2 NULL
NULL 3
NULL 4
5 5
6 6
6 6
6 6
6 6
Solusi UNION:
SELECT * FROM cte_t1 t1 LEFT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
UNION
SELECT * FROM cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
Memberikan jawaban yang salah:
id1 id2
NULL 3
NULL 4
1 NULL
2 NULL
5 5
6 6
Solusi UNION ALL:
SELECT * FROM cte_t1 t1 LEFT OUTER join cte_t2 t2 ON t1.id1 = t2.id2
UNION ALL
SELECT * FROM cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
Juga salah.
id1 id2
1 NULL
2 NULL
5 5
6 6
6 6
6 6
6 6
NULL 3
NULL 4
5 5
6 6
6 6
6 6
6 6
Sedangkan kueri ini:
SELECT t1.*, t2.*
FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp
LEFT JOIN t1 ON t1.id = tmp.id
LEFT JOIN t2 ON t2.id = tmp.id;
Memberi yang berikut:
id1 id2
1 NULL
2 NULL
NULL 3
NULL 4
5 5
6 6
6 6
6 6
6 6
Urutannya berbeda, tetapi jika tidak cocok dengan jawaban yang benar.
UNION ALL
solusinya. Juga, ini menyajikan solusi menggunakan UNION
yang akan lebih lambat pada tabel sumber besar karena de-duplikasi yang diperlukan. Akhirnya, itu tidak akan dikompilasi, karena bidang id
itu tidak ada di subquery tmp
.
UNION ALL
Solusinya: ... Juga salah." Kode yang Anda tampilkan menghilangkan intersection-exclusion dari right-join ( where t1.id1 is null
) yang harus disediakan di UNION ALL
. Dengan kata lain, solusi Anda mengalahkan semua yang lain, hanya ketika salah satu solusi lainnya tidak diimplementasikan dengan benar. Pada "kelucuan," poin diambil. Itu serampangan, permintaan maaf saya.
Standar SQL mengatakan full join on
adalahinner join on
baris union all
tertandingi baris tabel kiri diperpanjang oleh nulls union all
kanan baris tabel diperpanjang oleh nulls. Yakni inner join on
baris union all
dalam left join on
tapi bukan inner join on
union all
baris right join on
tapi tidak inner join on
.
Yaitu left join on
barisunion all
right join on
tidak masuk inner join on
. Atau jika Anda tahu inner join on
hasil Anda tidak dapat memiliki nol di kolom tabel kanan tertentu maka " right join on
baris tidak dalam inner join on
" adalah baris right join on
dengan on
kondisi yang diperpanjang oleh and
kolom ituis null
.
Yaitu right join on
union all
sesuai jugaleft join on
baris yang .
Dari Apa perbedaan antara "INNER JOIN" dan "OUTER JOIN"? :
(SQL Standard 2006 SQL / Foundation 7.7 Sintaks Aturan 1, Aturan Umum 1 b, 3 c & d, 5 b.)