MySQL'de Tam Dış Birleştirme yapmak istiyorum. Mümkün mü? Tam Dış Birleştirme MySQL tarafından destekleniyor mu?
MySQL'de Tam Dış Birleştirme yapmak istiyorum. Mümkün mü? Tam Dış Birleştirme MySQL tarafından destekleniyor mu?
Yanıtlar:
MySQL'de FULL JOINS'iniz yok, ancak onları taklit edebileceğinizden emin olabilirsiniz .
Bu SO sorudan kopyalanan bir örnek ÖRNEK için var:
iki tablo ile 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
Yukarıdaki sorgu, TAM ÇIKIŞ BİRLEŞTİRME işleminin yinelenen satır üretmeyeceği özel durumlar için geçerlidir. Yukarıdaki sorgu UNION
, sorgu deseni tarafından tanıtılan yinelenen satırları kaldırmak için ayarlanan operatöre bağlıdır . İkinci sorgu için bir anti-birleştirme deseni kullanarak yinelenen satırlar girmekten kaçınabiliriz ve daha sonra iki seti birleştirmek için bir UNION ALL set operatörü kullanabiliriz. Bir FULL OUTER JOIN'in yinelenen satırları döndüreceği daha genel bir durumda, bunu yapabiliriz:
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
ve içinde yinelenen satır yoksa, bu yanıttaki t2
sorgu TAM DIŞ BİRLEŞTİRME öykünen bir sonuç kümesi döndürür. Ama daha genel durumda, örneğin, SEÇ liste dönen satırları benzersiz yapmak için yeterli sütunlar / ifadeler içermiyor, sonra bu sorgu kalıptır yetersiz bir üreteceği setini çoğaltmak FULL OUTER JOIN
. Daha sadık bir öykünme elde etmek için, UNION ALL
ayarlanmış bir operatöre ihtiyacımız olacak ve sorgulardan birinin bir anti-birleştirme modeline ihtiyacı olacaktır . Pavle Lekic'ten (yukarıdaki) yorum doğru sorgu desenini verir .
Pablo Santa Cruz'un verdiği cevap doğrudur; ancak, herhangi birinin bu sayfada tökezlemesi ve daha fazla açıklama istemesi durumunda, ayrıntılı bir döküm.
Aşağıdaki tablolara sahip olduğumuzu varsayalım:
-- t1
id name
1 Tim
2 Marta
-- t2
id name
1 Tim
3 Katarina
Bir iç birleşim, şöyle:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
Yalnızca her iki tabloda da görünen kayıtları alalım, şöyle:
1 Tim 1 Tim
İç birleşimlerin bir yönü yoktur (sol veya sağ gibi), açıkça çift yönlüdürler - her iki tarafta da bir eşleşmeye ihtiyacımız var.
Dış birleşimler, diğer tabloda eşleşmeyebilecek kayıtları bulmak içindir. Bu nedenle, birleştirmenin hangi tarafında eksik kayıt olmasına izin verileceğini belirtmeniz gerekir.
LEFT JOIN
ve RIGHT JOIN
kısaltmasıdır edilir LEFT OUTER JOIN
ve RIGHT OUTER JOIN
; Dış birleşimler ve iç birleşimler kavramlarını güçlendirmek için aşağıdaki tam adlarını kullanacağım.
Sol bir dış birleşim, şöyle:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... sağdaki tabloda bir eşleşme olup olmadıklarına bakılmaksızın, soldaki tablodaki tüm kayıtları alacağız, şöyle:
1 Tim 1 Tim
2 Marta NULL NULL
Sağ dış birleşim, şöyle:
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... soldaki tabloda bir eşleşme olup olmadıklarına bakılmaksızın, sağdaki tüm kayıtları bize aşağıdaki gibi alır:
1 Tim 1 Tim
NULL NULL 3 Katarina
Tam bir dış birleşim, diğer tabloda bir eşleşme olsun ya da olmasın, her iki tabloda da tüm kayıtları verir; her iki tarafta da eşleşmenin olmadığı NULL'lar. Sonuç şöyle görünecektir:
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
Ancak, Pablo Santa Cruz'un işaret ettiği gibi, MySQL bunu desteklemiyor. Sol birleşim ve sağ birleşim birliği yaparak böyle taklit edebiliriz:
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`;
UNION
"Bu sorguların ikisini de çalıştır, sonra sonuçları üst üste yığar" anlamında düşünebilirsin ; satırların bazıları ilk sorgudan, bazıları ise ikinci sorgudan gelecek.
Unutulmamalıdır ki UNION
, MySQL'deki a , tam kopyaları ortadan kaldıracaktır: Tim, her iki sorguda da görünecektir, ancak UNION
yalnızca bir kez onu listeliyor. Veritabanı gurusu meslektaşım bu davranışa güvenilmemesi gerektiğini düşünüyor. Bu yüzden daha açık WHERE
olmak gerekirse, ikinci sorguya bir madde ekleyebiliriz :
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;
Eğer Öte yandan, istediği nedense çiftleri görmek için şunu kullanabilirsiniz UNION ALL
.
FULL OUTER JOIN
. Bu şekilde sorgulama yapmak ve bu kopyaları kaldırmak için UNION kullanmak yanlış değildir. Ancak a'yı gerçekten çoğaltmak için FULL OUTER JOIN
, anti-birleştirme için sorgulardan birine ihtiyacımız var.
UNION
işlemin bu kopyaları kaldıracağı doğrudur ; ancak TAM DIŞ BİRLEŞTİRME tarafından döndürülen yinelenen satırlar da dahil olmak üzere TÜM yinelenen satırları kaldırır. Taklit etmek a FULL JOIN b
için doğru desen (a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
.
Bir union
sorgu kullanılması yinelenenleri kaldırır ve bu full outer join
işlemin davranışından farklıdır ve yinelenenleri asla kaldırmaz:
[Table: t1] [Table: t2]
value value
------- -------
1 1
2 2
4 2
4 5
Bu beklenen sonuçtur full outer join
:
value | value
------+-------
1 | 1
2 | 2
2 | 2
Null | 5
4 | Null
4 | Null
Bu, aşağıdakilerle birlikte left
ve kullanmanın sonucudur :right Join
union
value | value
------+-------
Null | 5
1 | 1
2 | 2
4 | Null
Önerdiğim sorgu:
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
Beklenen sonuçla aynı olan yukarıdaki sorgunun sonucu:
value | value
------+-------
1 | 1
2 | 2
2 | 2
4 | NULL
4 | NULL
NULL | 5
@Steve Chambers : [Yorumlardan, çok teşekkürler!]
Not: Bu hem verimlilik hem de aynı sonuçları üretmek için en iyi çözüm olabilirFULL OUTER JOIN
. Bu blog gönderisi ayrıca iyi açıklıyor - Yöntem 2'den alıntı yapmak için: "Bu, yinelenen satırları doğru bir şekilde işler ve olmaması gereken hiçbir şey içermez.UNION ALL
DüzUNION
tutmak yerine, kullanmak istediğim kopyaları ortadan kaldırmak gerekir. yinelenenleri sıralamaya ve kaldırmaya gerek olmadığından büyük sonuç kümelerinde önemli ölçüde daha verimli olabilir. "
full outer join
Görselleştirme ve matematikten gelen başka bir çözüm eklemeye karar verdim , yukarıda daha iyi değil daha okunabilir:
Tam dış araçları katılmak
(t1 ∪ t2)
tüm:t1
veyat2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only
her ikisi de bütün:t1
vet2
artı tümt1
bu olmayant2
ve artı tümt2
bu değildirt1
:
-- (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
. Bu blog yazısı da iyi açıklıyor - Yöntem 2'den alıntı yapmak için: "Bu, yinelenen satırları doğru bir şekilde işler ve olmaması gereken hiçbir şeyi içermez. İstediğim kopyaları ortadan kaldıracak düz UNION yerine UNION ALL kullanmak gerekir Yinelenenleri sıralamaya ve kaldırmaya gerek olmadığından bu, büyük sonuç kümelerinde önemli ölçüde daha etkili olabilir. "
MySql'de FULL-OUTER-JOIN sözdizimi yoktur. Hem LEFT JOIN hem de RIGHT JOIN yaparak aşağıdaki gibi taklit etmelisiniz.
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
Ancak MySql'de RIGHT JOIN sözdizimi de yoktur. MySql'in dış birleştirme basitleştirmesine göre, sağ birleştirme, sorgudaki t1 ve t2'yi FROM
ve ON
sorgudaki yan tümceyi değiştirerek eşdeğer sol birleştirmeye dönüştürülür . Böylece, MySql Sorgu Optimize Edici orijinal sorguyu aşağıdakine çevirir -
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
Şimdi olduğu gibi orijinal sorgu yazma bir zarar bir WHERE yan tümcesi gibi yüklemler varsa demek ama, orada katılmadan önce- on yüklemi veya bir VE yüklemi ON
bir olan fıkra, katılmak sırasında- yüklem, sonra sizi şeytana bir göz atmak isteyebilir; ayrıntılarda.
MySql sorgu optimizatörü, tahminleri boş olarak reddedilirse rutin olarak kontrol eder . Şimdi, DOĞRU BİRLEŞTİRME yaptıysanız, ancak t1 sütunundaki NEREDE yordamı ile, reddedilen bir senaryoya girme riskiyle karşı karşıya kalabilirsiniz .
Örneğin, aşağıdaki sorgu -
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'
Sorgu Optimize Edici tarafından aşağıdakilere çevrilir-
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'
Böylece tabloların sırası değişti, ancak yüklem t1'e hala uygulandı, ancak t1 şimdi 'ON' yan tümcesinde. T1.col1 NOT NULL
sütunu olarak tanımlanırsa , bu sorgu null reddedilir .
Boş olarak reddedilen herhangi bir dış birleşim (sol, sağ, dolu) MySql tarafından iç birleşime dönüştürülür.
Bu nedenle, beklediğiniz sonuçlar MySql'in döndürdüğünden tamamen farklı olabilir. MySql'in RIGHT JOIN ile ilgili bir hata olduğunu düşünebilirsiniz, ama bu doğru değil. Sadece MySql sorgu optimize edici nasıl çalışır. Bu yüzden sorumlu geliştirici, sorguyu oluştururken bu nüanslara dikkat etmek zorundadır.
SQLite'de şunları yapmalısınız:
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
Yukarıdaki cevapların hiçbiri gerçekten doğru değildir, çünkü yinelenen değerler olduğunda anlambilimleri takip etmezler.
(Bu kopyadan ) gibi bir sorgu için :
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.Name = t2.Name;
Doğru eşdeğer:
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;
Bunun NULL
değerlerle çalışması için gerekirse (bu da gerekli olabilir), bunun yerine NULL
-safe karşılaştırma operatörünü kullanın .<=>
=
FULL OUTER JOIN
olduğunda bir kereden farklı sonuçlar verebilir name
. union all
Anti-birleştirme deseni ile Sorgu dış doğru davranışı katılması yeniden gerekir, ancak çözelti daha uygun bir bağlama ve tablolar üzerinde etkin olan kısıtlamalar bulunmamasına bağlıdır.
union all
, ancak bu cevap, mevcut kopyaları tutacak ancak yenilerini eklemeyi önleyen birinci veya ikinci sorguda anti-birleştirme desenini kaçırıyor. Bağlama bağlı olarak diğer çözümler (bunun gibi) daha uygun olabilir.
Değiştirilmiş shA.t sorgusu daha fazla netlik için:
-- 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
Cross Join Solution hakkında ne söyledin ?
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;
select (select count(*) from t1) * (select count(*) from t2))
ve sonuç kümesindeki satırlarla birlikte tüm olası kombinasyonların kümesini verecektir .
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
Cevabı düzeltirim ve işler tüm satırları içerir (Pavle Lekic'in cevabına dayanarak)
(
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
eşleşme olmayan satırları döndürecek tableb
ve tam tersi bir tür "yalnızca dış" birleştirme türüdür . UNION ALL
Yalnızca bu iki tabloda eşit sıralı sütunlar varsa ve bu garanti edilmediğinde çalışırsınız .
Cevap:
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;
Aşağıdaki gibi yeniden oluşturulabilir:
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;
Bir UNION veya UNION ALL yanıtının kullanılması, temel tabloların yinelenen girişleri olduğu kenar durumunu kapsamaz.
Açıklama:
BİRLİĞİ veya BİRLİĞİ TÜMÜNÜN kaplayamayacağı bir uç dava var. TAM DIŞ BAĞLANTILARI desteklemediğinden bunu mysql'de test edemeyiz, ancak bunu destekleyen bir veritabanında gösterebiliriz:
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
UNION çözümü:
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
Yanlış bir cevap verir:
id1 id2
NULL 3
NULL 4
1 NULL
2 NULL
5 5
6 6
UNION ALL çözümü:
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
Ayrıca yanlış.
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
Halbuki bu sorgu:
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;
Aşağıdakileri verir:
id1 id2
1 NULL
2 NULL
NULL 3
NULL 4
5 5
6 6
6 6
6 6
6 6
Sıra farklı, ancak aksi takdirde doğru cevapla eşleşiyor.
UNION ALL
çözümü yanlış tanıtıyor . Ayrıca, UNION
gerekli tekilleştirme nedeniyle büyük kaynak tablolarda daha yavaş kullanılacak bir çözüm sunar . Son olarak, derlenmeyecektir, çünkü alan id
alt sorguda mevcut değildir tmp
.
UNION ALL
Çözüm: ... Ayrıca yanlış." Sunduğunuz kod where t1.id1 is null
, içinde sağlanması gereken sağ birleştirme ( ) yönteminden kesişim-hariç tutma özelliğini dışarıda bırakır UNION ALL
. Yani çözümünüz, diğer çözümlerden biri yanlış uygulandığında diğerlerini çöker. "Sevimlilik" noktasında. Bu çok minnettardı, özür dilerim.
SQL standart diyor full join on
olduğunu inner join on
satırları union all
boş değerlere uzatılan eşsiz sol tablo satırları union all
sağ tablo satırları boş değerlere uzatılabilir. Yani inner join on
satırlar union all
satırları left join on
ancak inner join on
union all
satırları right join on
değil inner join on
.
Yani left join on
satırlar union all
right join on
içeride değil inner join on
. Eğer biliyorsanız Veya inner join on
sonucun "daha sonra belirli bir sağ tablo sütununda boş değer içeremez right join on
değil satırları inner join on
" in satırlarıdır right join on
ile on
uzatılabilir durumda and
o sütun is null
.
Benzer şekilde right join on
union all
uygun left join on
satırlar.
Kimden “INNER JOIN” ve “OUTER JOIN” arasındaki fark nedir? :
(SQL Standard 2006 SQL / Foundation 7.7 Sözdizimi Kuralları 1, Genel Kurallar 1 b, 3 c & d, 5 b.)