ทำอย่างไรจึงจะเข้าร่วมเต็มที่ใน MySQL?


654

ฉันต้องการเข้าร่วมเต็มรูปแบบใน MySQL เป็นไปได้ไหม เต็มภายนอกเข้าร่วมสนับสนุนโดย MySQL หรือไม่


2
เป็นไปได้ที่ซ้ำกันของข้อผิดพลาดไวยากรณ์ MySQL Full Outer Join Join
Joe Stefanelli

4
คำถามนี้มีคำตอบที่ดีกว่า
Julio Marins

ระวังคำตอบที่นี่ มาตรฐาน SQL บอกว่าการเข้าร่วมเต็มรูปแบบคือการเข้าร่วมภายในกับสหภาพแถวแถวโต๊ะซ้ายที่ไม่มีใครเทียบทั้งหมดที่ขยายโดย nulls สหภาพแถวตารางขวาทั้งหมดขยายด้วย nulls คำตอบส่วนใหญ่ที่นี่ผิด (ดูความคิดเห็น) และคำที่ไม่ผิดอย่าจัดการกับกรณีทั่วไป แม้ว่าจะมีจำนวนโหวตมาก (ไม่ยุติธรรม) (ดูคำตอบของฉัน.)
philipxy

จะเกิดอะไรขึ้นเมื่อคุณพยายามเข้าร่วมโดยคีย์ที่ไม่ใช่หลัก / คอลัมน์ที่จัดกลุ่ม เช่นฉันมีแบบสอบถามการขายต่อรัฐ "รัฐ", "ขาย" และค่าใช้จ่ายอื่น ๆ ต่อรัฐ "รัฐ", "ค่าใช้จ่าย", แบบสอบถามทั้งสองใช้กลุ่มโดย ("รัฐ") เมื่อฉันทำการรวมกันระหว่างด้านซ้ายและขวาเชื่อมต่อระหว่างสองแบบสอบถามฉันได้รับสองสามแถวที่มีขาย แต่ไม่มีค่าใช้จ่ายอีกสองสามค่าใช้จ่าย แต่ไม่มีขายทุกอย่างจนถึงจุดนี้ แต่ฉันก็มีน้อยด้วย ขายและค่าใช้จ่ายและทำซ้ำ "รัฐ" คอลัมน์ ... ไม่มากของปัญหา แต่ไม่ได้รู้สึกขวา ...
Jairo ซาโน

1
@JairoLozano ไม่จำเป็นต้องมีข้อ จำกัด ในการสืบค้น แม้ว่าเมื่อมีข้อ จำกัด ถือแบบสอบถามเพิ่มเติมกลับคำตอบที่ต้องการมิฉะนั้นจะไม่ ข้อ จำกัด ไม่มีผลต่อการเข้าร่วมเต็มรูปแบบในการส่งคืนสำหรับอาร์กิวเมนต์ที่กำหนด ปัญหาที่คุณอธิบายคือแบบสอบถามที่คุณเขียนนั้นเป็นแบบสอบถามที่ไม่ถูกต้อง (สันนิษฐานว่าเป็นข้อผิดพลาดทั่วไปที่ผู้คนต้องการเข้าร่วมบางคนอาจเกี่ยวข้องกับคีย์ที่แตกต่างกันของบางแบบสอบถามย่อยแต่ละคนอาจเกี่ยวข้องกับการเข้าร่วมและ / หรือการรวม แต่พวกเขาพยายามที่จะเข้าร่วมทั้งหมดแล้วรวมหรือรวม .)
philipxy

คำตอบ:


669

คุณไม่มีการเข้าร่วมเต็มรูปแบบบน MySQL แต่คุณสามารถเลียนแบบพวกเขาได้

สำหรับรหัสตัวอย่างที่ถอดความจากคำถาม SO นี้คุณมี:

ด้วยสองตาราง 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

แบบสอบถามด้านบนใช้สำหรับกรณีพิเศษที่การดำเนินการ JOIN FULL OUTER JOIN จะไม่สร้างแถวที่ซ้ำกัน แบบสอบถามด้านบนขึ้นอยู่กับตัวUNIONดำเนินการตั้งค่าเพื่อลบแถวที่ซ้ำกันที่แนะนำโดยรูปแบบแบบสอบถาม เราสามารถหลีกเลี่ยงการแนะนำแถวที่ซ้ำกันโดยใช้รูปแบบการต่อต้านการเข้าร่วมสำหรับการสืบค้นที่สองจากนั้นใช้ตัวดำเนินการชุด UNION ALL เพื่อรวมสองชุดเข้าด้วยกัน ในกรณีทั่วไปมากขึ้นที่การรวมเอาเต็มจะกลับแถวที่ซ้ำกันเราสามารถทำได้:

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

33
ที่จริงสิ่งที่คุณเขียนไม่ถูกต้อง เพราะเมื่อคุณทำ UNION คุณจะลบรายการที่ซ้ำกันและบางครั้งเมื่อคุณเข้าร่วมสองตารางที่แตกต่างกัน
Pavle Lekic

158
นี่เป็นตัวอย่างที่ถูกต้อง:(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
Pavle Lekic

8
ดังนั้นความแตกต่างคือฉันกำลังเข้าร่วมการรวมซ้ายแล้วสิทธิพิเศษโดยใช้ยูเนี่ยนทั้งหมด
Pavle Lekic

5
และตอนนี้ฉันเห็นว่าคุณพูดด้วยตัวเองขอโทษ บางทีคุณสามารถอัปเดตคำตอบของคุณได้เนื่องจากมีกรณีนี้ว่ามันผิดและยูเนี่ยนทั้งหมดก็จะมีประสิทธิภาพมากขึ้นอยู่เสมอ?
ysth

10
@ypercube: หากไม่มีแถวที่ซ้ำกันในt1และt2แบบสอบถามในคำตอบนี้จะส่งกลับ resultset ที่เลียนแบบ FULL OUTER JOIN แต่ในกรณีทั่วไปมากขึ้นตัวอย่างเช่นรายการ SELECT ไม่ได้มีคอลัมน์เพียงพอ / นิพจน์ที่จะทำให้แถวกลับไม่ซ้ำกันแล้วรูปแบบแบบสอบถามนี้ไม่เพียงพอFULL OUTER JOINที่จะทำให้เกิดการตั้งค่าที่จะได้รับการผลิตโดย เพื่อให้ได้การจำลองที่ซื่อสัตย์ยิ่งขึ้นเราต้องการตัวUNION ALLดำเนินการชุดหนึ่งและหนึ่งในแบบสอบถามจะต้องมีรูปแบบการต่อต้านการเข้าร่วม ความคิดเห็นจากPavle Lekic (ด้านบน) ให้รูปแบบแบบสอบถามที่ถูกต้อง
spencer7593

351

คำตอบที่Pablo Santa Cruzให้นั้นถูกต้อง อย่างไรก็ตามในกรณีที่ใครก็ตามที่เข้ามาในหน้านี้และต้องการคำชี้แจงเพิ่มเติมนี่คือรายละเอียด

ตารางตัวอย่าง

สมมติว่าเรามีตารางต่อไปนี้:

-- t1
id  name
1   Tim
2   Marta

-- t2
id  name
1   Tim
3   Katarina

เข้าร่วม Inner

การเข้าร่วมภายในเช่นนี้:

SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

จะให้เรามีเพียงระเบียนที่ปรากฏในตารางทั้งสองแบบนี้:

1 Tim  1 Tim

การรวมภายในไม่มีทิศทาง (เช่นซ้ายหรือขวา) เนื่องจากมีทิศทางสองทิศทางอย่างชัดเจน - เราต้องการการแข่งขันทั้งสองด้าน

เข้าร่วมภายนอก

ในทางกลับกันการรวมภายนอกนั้นใช้สำหรับค้นหาระเบียนที่อาจไม่มีการแข่งขันในตารางอื่น ดังนั้นคุณต้องระบุที่ด้านข้างของเข้าร่วมได้รับอนุญาตให้มีการบันทึกที่ขาดหายไป

LEFT JOINและRIGHT JOINจดชวเลขLEFT OUTER JOINและRIGHT OUTER JOIN; ฉันจะใช้ชื่อเต็มของพวกเขาด้านล่างเพื่อเสริมแนวคิดของการรวมภายนอกกับการรวมภายใน

เข้าร่วม Outer Left

การเข้าร่วมด้านนอกด้านซ้ายเช่นนี้:

SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

... จะให้เราทุกระเบียนจากตารางด้านซ้ายโดยไม่คำนึงว่าพวกเขามีการแข่งขันในตารางด้านขวาเช่นนี้:

1 Tim   1    Tim
2 Marta NULL NULL

เข้าร่วม Outer ขวา

การเข้าร่วมภายนอกที่ถูกต้องเช่นนี้:

SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;

... จะให้เราทุกระเบียนจากตารางด้านขวาโดยไม่คำนึงว่าพวกเขามีการแข่งขันในตารางด้านซ้ายเช่นนี้

1    Tim   1  Tim
NULL NULL  3  Katarina

เต็มนอกเข้าร่วม

การเข้าร่วมรอบนอกเต็มรูปแบบจะให้ระเบียนทั้งหมดจากทั้งสองตารางไม่ว่าจะมีการแข่งขันในตารางอื่นหรือไม่โดยมี NULL ทั้งสองด้านที่ไม่มีการแข่งขัน ผลลัพธ์จะเป็นดังนี้:

1    Tim   1    Tim
2    Marta NULL NULL
NULL NULL  3    Katarina

อย่างไรก็ตามตามที่ Pablo Santa Cruz ชี้ให้เห็นว่า MySQL ไม่รองรับสิ่งนี้ เราสามารถเลียนแบบได้ด้วยการเข้าร่วม UNION จากซ้ายเข้าร่วมและเข้าร่วมขวาเช่นนี้

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ความหมายเป็น "เรียกใช้แบบสอบถามทั้งสองนี้แล้วซ้อนผลลัพธ์ที่ด้านบนของกันและกัน"; แถวบางแถวจะมาจากคิวรีแรกและบางแถวจากแถวที่สอง

ควรสังเกตว่าUNIONใน MySQL จะกำจัดรายการที่ซ้ำกัน: Tim จะปรากฏในทั้งสองแบบสอบถามที่นี่ แต่ผลลัพธ์ของUNIONรายการเดียวเขาครั้งเดียว เพื่อนร่วมงานกูรูฐานข้อมูลของฉันรู้สึกว่าพฤติกรรมนี้ไม่ควรเชื่อถือ เพื่อให้ชัดเจนยิ่งขึ้นเกี่ยวกับเรื่องนี้เราสามารถเพิ่มส่วนWHEREคำสั่งในแบบสอบถามที่สอง:

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;

ในทางกลับกันถ้าคุณต้องการUNION ALLที่จะเห็นรายการที่ซ้ำกันด้วยเหตุผลบางอย่างที่คุณสามารถใช้


4
สำหรับ MySQL คุณต้องการหลีกเลี่ยงการใช้ UNION แทน UNION ALL หากไม่มีการซ้อนทับกัน (ดูความคิดเห็นของ Pavle ด้านบน) หากคุณสามารถเพิ่มข้อมูลเพิ่มเติมลงในเอฟเฟกต์นั้นในคำตอบของคุณที่นี่ฉันคิดว่ามันจะเป็นคำตอบที่ต้องการสำหรับคำถามนี้เนื่องจากละเอียดมากขึ้น
Garen

2
คำแนะนำจาก "เพื่อนร่วมงานกูรูฐานข้อมูล" ถูกต้อง ในแง่ของรูปแบบเชิงสัมพันธ์ (งานเชิงทฤษฎีทั้งหมดที่ทำโดย Ted Codd และ Chris Date) แบบสอบถามของรูปแบบสุดท้ายเลียนแบบ FULL OUTER JOIN เพราะรวมสองชุดที่แตกต่างกันแบบสอบถามที่สองไม่แนะนำ "ซ้ำกัน" ( แถวกลับมาแล้วโดยการสอบถามครั้งแรก) FULL OUTER JOINที่จะไม่ได้รับการผลิตโดย ไม่มีอะไรผิดปกติในการทำแบบสอบถามด้วยวิธีนี้และใช้ UNION เพื่อลบรายการที่ซ้ำกัน แต่เพื่อทำซ้ำ a จริง ๆFULL OUTER JOINเราต้องการหนึ่งในแบบสอบถามเพื่อต่อต้านการเข้าร่วม
spencer7593

1
@IstiaqueAhmed: เป้าหมายคือเลียนแบบการดำเนินการเข้าร่วมแบบเต็มด้านนอก เราต้องการเงื่อนไขนั้นในแบบสอบถามที่สองเพื่อให้ส่งกลับเฉพาะแถวที่ไม่มีการจับคู่ (รูปแบบการต่อต้านการเข้าร่วม) หากไม่มีเงื่อนไขนั้นแบบสอบถามจะเป็นการรวมภายนอก ... โดยจะส่งคืนแถวที่ตรงกันเช่นเดียวกับที่ไม่มีการจับคู่ และแถวที่ตรงกับที่ได้รับแล้วส่งกลับโดยแบบสอบถามแรก หากแบบสอบถามที่สองส่งคืนแถวเดียวกันเหล่านั้น (อีกครั้ง) เราได้ทำซ้ำแถวและผลลัพธ์ของเราจะไม่เทียบเท่ากับการเข้าร่วมแบบเต็มด้านนอก
spencer7593

1
@IstiaqueAhmed: เป็นความจริงที่ว่าการUNIONดำเนินการจะลบข้อมูลที่ซ้ำกันออกไป แต่มันยังลบแถวที่ซ้ำกันทั้งหมดรวมถึงแถวที่ซ้ำกันซึ่งจะอยู่ในคืนโดย FULL OUTER JOIN ที่จะเลียนแบบรูปแบบที่ถูกต้องคือa FULL JOIN b (a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
spencer7593

1
คำตอบที่กระชับมากพร้อมคำอธิบายที่ดี ขอบคุณสำหรับสิ่งนี้.
Najeeb

35

การใช้unionคิวรีจะเป็นการลบรายการที่ซ้ำกันและสิ่งนี้แตกต่างจากพฤติกรรมfull outer joinที่ไม่เคยลบสิ่งที่ซ้ำกัน:

[Table: t1]                            [Table: t2]
value                                  value
-------                                -------
1                                      1
2                                      2
4                                      2
4                                      5

นี่คือผลลัพธ์ที่คาดหวังของfull outer join:

value | value
------+-------
1     | 1
2     | 2
2     | 2
Null  | 5
4     | Null
4     | Null

นี่คือผลลัพธ์ของการใช้leftและright Joinด้วยunion:

value | value
------+-------
Null  | 5 
1     | 1
2     | 2
4     | Null

[SQL Fiddle]

ข้อความค้นหาที่แนะนำของฉันคือ:

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 

ผลลัพธ์ของข้อความค้นหาข้างต้นที่เหมือนกับผลลัพธ์ที่คาดไว้:

value | value
------+-------
1     | 1
2     | 2
2     | 2
4     | NULL
4     | NULL
NULL  | 5

[SQL Fiddle]


@ Steve Chambers : [จากการแสดงความคิดเห็นด้วยขอบคุณมาก!]
หมายเหตุ:FULL OUTER JOINนี่อาจจะเป็นทางออกที่ดีที่สุดทั้งสำหรับประสิทธิภาพและการสร้างผลเช่นเดียวกับ โพสต์บล็อกนี้ยังอธิบายได้เป็นอย่างดี - อ้างจากวิธีที่ 2: "นี่จัดการแถวที่ซ้ำกันอย่างถูกต้องและไม่รวมสิ่งใด ๆ ที่มันไม่ควรจำเป็นต้องใช้UNION ALLแทนธรรมดาUNIONซึ่งจะกำจัดสิ่งที่ซ้ำกันที่ฉันต้องการเก็บไว้ อาจมีประสิทธิภาพมากขึ้นในชุดผลลัพธ์ขนาดใหญ่เนื่องจากไม่จำเป็นต้องเรียงลำดับและลบรายการซ้ำ "


ฉันตัดสินใจเพิ่มโซลูชันอื่นที่มาจากการfull outer joinสร้างภาพและคณิตศาสตร์มันไม่ได้ดีไปกว่านั้น แต่อ่านได้มากขึ้น:

การเข้าร่วมภายนอกเต็มรูปแบบหมายถึง(t1 ∪ t2): ทั้งหมดในt1หรือในt2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only: ทั้งหมดในทั้งt1และt2รวมทั้งหมดในt1ที่ไม่ได้อยู่ในt2และบวกทั้งหมดในt2ที่ไม่ได้อยู่ในt1:

-- (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)

[SQL Fiddle]


เรากำลังทำภารกิจลากจูงเหมือนกันหากมีคิวย่อยสำหรับ t1 และ t2 แล้ว mysql จะต้องทำงานซ้ำอีกครั้งใช่มั้ย? เราสามารถลบสิ่งนี้โดยใช้นามแฝงในสถานการณ์นี้ได้ไหม:
Kabir Hossain

ฉันแนะนำให้คุณใช้ตารางชั่วคราว;)
shA.t

5
FULL OUTER JOINวิธีการนี้น่าจะเป็นทางออกที่ดีที่สุดทั้งสำหรับประสิทธิภาพและการสร้างผลเช่นเดียวกับ โพสต์บล็อกนี้ยังอธิบายได้เป็นอย่างดี - การอ้างอิงจากวิธีที่ 2: "นี่จัดการแถวที่ซ้ำกันอย่างถูกต้องและไม่รวมสิ่งใดที่ไม่ควรใช้ยูเนี่ยนทั้งหมดแทนยูเนี่ยนธรรมดาซึ่งจะกำจัดสิ่งที่ซ้ำกันที่ฉันต้องการ เก็บไว้ซึ่งอาจมีประสิทธิภาพมากขึ้นในชุดผลลัพธ์ขนาดใหญ่เนื่องจากไม่จำเป็นต้องเรียงลำดับและลบรายการที่ซ้ำกัน "
Steve Chambers

2
@ SteveChambers มันสายเกินไป แต่ขอบคุณสำหรับความคิดเห็นของคุณ ฉันเพิ่มความคิดเห็นของคุณแล้วตอบเพื่อเน้นมากขึ้นถ้าคุณไม่เห็นด้วยโปรดย้อนกลับ;)
shA.t

ไม่มีปัญหา @ shA.t - IMO สิ่งนี้ควรมี upvotes และ / หรือเป็นคำตอบที่ยอมรับได้จริง ๆ
Steve Chambers

6

MySql ไม่มีไวยากรณ์ FULL-OUTER-JOIN คุณต้องเลียนแบบโดยทำทั้งซ้ายและขวาเข้าร่วมดังต่อไปนี้ -

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id  
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id

แต่ MySql ยังไม่มีไวยากรณ์ RIGHT JOIN ตามการทำให้การรวมภายนอกของ MySql ง่ายขึ้นการเข้าร่วมที่เหมาะสมจะถูกแปลงเป็นการเข้าร่วมด้านซ้ายที่เทียบเท่าโดยการสลับ t1 และ t2 ในFROMและONclause ในแบบสอบถาม ดังนั้น MySql Query Optimizer แปลแบบสอบถามต้นฉบับเป็นดังต่อไปนี้ -

SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id  
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id

ตอนนี้ไม่มีอันตรายใด ๆ ในการเขียนแบบสอบถามต้นฉบับตามที่เป็นอยู่ แต่บอกว่าถ้าคุณมีภาคแสดงเช่นคำสั่งย่อย WHERE ซึ่งเป็นภาคก่อนที่จะเข้าร่วมหรือคำกริยาและคำกริยาและONประโยคที่อยู่ในระหว่างเข้าร่วมแล้วคุณ อาจต้องการดูปีศาจ ซึ่งอยู่ในรายละเอียด

เครื่องมือเพิ่มประสิทธิภาพคิวรี MySql จะตรวจสอบเพรดิเคตเป็นประจำหากพวกเขาปฏิเสธด้วยค่า null นิยามและตัวอย่างที่ไม่ได้รับการปฏิเสธ ตอนนี้ถ้าคุณทำ RIGHT JOIN แต่ด้วย WHERE predicate บนคอลัมน์จาก t1 คุณอาจมีความเสี่ยงที่จะพบสถานการณ์ที่ปฏิเสธ

ตัวอย่างเช่นแบบสอบถามต่อไปนี้ -

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'

รับการแปลเป็นภาษาต่อไปนี้โดย Query Optimizer-

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'

ดังนั้นลำดับของตารางจึงเปลี่ยนไป แต่เพรดิเคตยังคงใช้กับ t1 แต่ตอนนี้ t1 อยู่ในส่วนคำสั่ง 'ON' หาก t1.col1 ถูกกำหนดให้เป็นNOT NULL คอลัมน์แล้วแบบสอบถามนี้จะเป็นโมฆะเป็นปฏิเสธ

การเข้าร่วมด้านนอกใด ๆ (ซ้าย, ขวา, เต็ม) ที่ถูกปฏิเสธเป็นโมฆะจะถูกแปลงเป็น MySql ที่เข้าร่วมวงใน

ดังนั้นผลลัพธ์ที่คุณอาจคาดหวังอาจแตกต่างอย่างสิ้นเชิงจากสิ่งที่ MySql ส่งคืน คุณอาจคิดว่ามันเป็นข้อผิดพลาดกับ RIGHT JOIN ของ MySql แต่นั่นไม่ถูกต้อง นี่เป็นเพียงการทำงานของเครื่องมือเพิ่มประสิทธิภาพแบบสอบถาม MySql ดังนั้นผู้พัฒนาที่รับผิดชอบจะต้องใส่ใจกับความแตกต่างเหล่านี้เมื่อเขาสร้างแบบสอบถาม


4

ใน SQLite คุณควรทำสิ่งนี้:

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

เราสามารถใช้มันได้หรือไม่ เช่นเดียวกับ: SELECT * จาก leftTable lt ซ้ายเข้าร่วม rightTable rt บน lt.id = rt.lrid ยูเนี่ยน SELECT lt. *, rl. * - เพื่อจับคู่ชุดคอลัมน์จาก leftTable lt RIGHT เข้าร่วม rt ON lt.id = rt.lrid ;
Kabir Hossain

ใช่ แต่ SQLite ไม่รองรับการเชื่อมต่อที่ถูกต้อง แต่ใช่ใน MYSQL ใช่
Rami Jamleh

4

ไม่มีคำตอบข้างต้นที่ถูกต้องจริงเพราะพวกเขาไม่ปฏิบัติตามความหมายเมื่อมีค่าซ้ำ

สำหรับแบบสอบถามเช่น (จากรายการที่ซ้ำกันนี้):

SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.Name = t2.Name;

เทียบเท่าที่ถูกต้องคือ:

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;

หากคุณต้องการสำหรับการทำงานที่มีNULLค่า (ซึ่งก็อาจจะเป็นสิ่งที่จำเป็น) จากนั้นใช้NULLตัวดำเนินการเปรียบเทียบ -Safe, มากกว่า<=>=


3
นี่มักจะเป็นทางออกที่ดี แต่อาจให้ผลลัพธ์ที่แตกต่างกว่าFULL OUTER JOINเมื่อใดก็ตามที่nameคอลัมน์ไม่มีค่า union allแบบสอบถามเข้าร่วมต่อต้านรูปแบบควรทำซ้ำเข้าร่วม outer พฤติกรรมอย่างถูกต้อง แต่วิธีการแก้ปัญหาที่มีความเหมาะสมขึ้นอยู่กับบริบทและข้อ จำกัด ที่มีการใช้งานบนโต๊ะ
fthiella

@fthiella . . นั่นเป็นจุดที่ดี ฉันปรับคำตอบ
Gordon Linoff

1
โอเค แต่ตัวดำเนินการเปรียบเทียบค่าว่างจะทำให้การเข้าร่วมสำเร็จซึ่งแตกต่างจากพฤติกรรมการรวมภายนอกแบบเต็มในกรณีที่คุณมีชื่อว่างทั้งใน t1 และใน t2
fthiella

@fthiella . . ฉันจะต้องคิดถึงวิธีที่ดีที่สุดในการทำสิ่งนี้ แต่เมื่อพิจารณาว่าคำตอบที่ยอมรับนั้นผิดเพียงใดเกือบทุกอย่างใกล้เคียงกับคำตอบที่ถูก (คำตอบนั้นผิดถ้ามีหลายปุ่มที่ด้านข้างทั้งสอง)
Gordon Linoff

1
ใช่คำตอบที่ยอมรับนั้นผิดเพราะเป็นวิธีแก้ปัญหาทั่วไปฉันคิดว่ามันถูกต้องที่จะใช้union allแต่คำตอบนั้นพลาดรูปแบบการต่อต้านการเข้าร่วมในการสืบค้นครั้งแรกหรือครั้งที่สองที่จะเก็บสำเนาที่มีอยู่เดิม ขึ้นอยู่กับบริบทโซลูชันอื่น ๆ (เช่นนี้) อาจเหมาะสมกว่า
fthiella

3

แก้ไขข้อความค้นหา shA.t เพื่อความชัดเจนมากขึ้น:

-- 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 

3

คุณสามารถทำสิ่งต่อไปนี้:

(SELECT 
    *
FROM
    table1 t1
        LEFT JOIN
    table2 t2 ON t1.id = t2.id
WHERE
    t2.id IS NULL)
UNION ALL
 (SELECT 
    *
FROM
    table1 t1
        RIGHT JOIN
    table2 t2 ON t1.id = t2.id
WHERE
    t1.id IS NULL);

1

คุณจะพูดอะไรเกี่ยวกับCross join solution

SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2 
ON 1=1;

2
ไม่นี่เป็นการเข้าร่วมไขว้ มันจะจับคู่ทุกแถวใน t1 กับทุกแถวใน t2 โดยให้ผลเป็นชุดของชุดค่าผสมที่เป็นไปได้ทั้งหมดกับselect (select count(*) from t1) * (select count(*) from t2))แถวในชุดผลลัพธ์
Marc L.

ในขณะที่รหัสนี้อาจจะตอบคำถามให้บริบทเพิ่มเติมเกี่ยวกับวิธีการและเหตุผลที่จะแก้ปัญหาที่เกิดขึ้นจะปรับปรุงคำตอบเป็นระยะยาวคุ้มค่า
Alexander

การเพิ่มซึ่งอาจจะมีประโยชน์? อาจเป็นตัวอย่าง?
Super Mario

0
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

0

เป็นไปได้เช่นกัน แต่คุณต้องพูดถึงชื่อเขตข้อมูลเดียวกันในการเลือก

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

นี่เป็นเพียงการทำซ้ำผลลัพธ์จากการเข้าร่วมด้านซ้าย
Matthew อ่าน

-1

ฉันแก้ไขการตอบสนองและผลงานรวมแถวทั้งหมด (ตามการตอบสนองของ 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ที่ไม่มีการแข่งขันในtablebและในทางกลับกัน คุณลองใช้UNION ALLซึ่งจะใช้ได้ก็ต่อเมื่อสองตารางนี้มีคอลัมน์ที่เรียงลำดับเท่ากันซึ่งไม่รับประกัน
Marc L.

มันทำงานได้ฉันสร้างบนฐานข้อมูล temp tablea (1,2,3,4,5,6) และ tableb (4,5,6,7,8,9) แถวมี 3 cols "id", "number" และ "NAME_NUMBER" เป็นข้อความและผลงานในผลมีเพียง (1,2,3,7,8,9)
RubénRuíz

1
นั่นไม่ใช่การเข้าร่วมภายนอก การเข้าร่วมภายนอกยังรวมถึงสมาชิกที่ตรงกัน
Marc L.

ประโยคใหม่นั้นมีผลทั้งหมด 1,2, ... , 9
RubénRuíz

-2

ตอบ:

SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;

สามารถสร้างใหม่ได้ดังนี้:

 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;

การใช้คำตอบ UNION หรือ UNION ทั้งหมดไม่ครอบคลุมกรณีขอบซึ่งตารางฐานมีรายการที่ซ้ำกัน

คำอธิบาย:

มีกรณีขอบที่ยูเนี่ยนหรือยูเนี่ยนทั้งหมดไม่สามารถครอบคลุม เราไม่สามารถทดสอบสิ่งนี้บน mysql ได้เนื่องจากมันไม่รองรับ FULL OUTER JOIN แต่เราสามารถแสดงให้เห็นได้ในฐานข้อมูลที่รองรับ:

 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:

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

ให้คำตอบที่ไม่ถูกต้อง:

 id1  id2
NULL  3
NULL  4
1  NULL
2  NULL
5  5
6  6

โซลูชัน 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

ยังไม่ถูกต้อง

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

โดยที่แบบสอบถามนี้:

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;

ให้สิ่งต่อไปนี้:

id1  id2
1  NULL
2  NULL
NULL  3
NULL  4
5  5
6  6
6  6
6  6
6  6

คำสั่งซื้อนั้นแตกต่างกัน แต่ตรงกับคำตอบที่ถูกต้อง


มันน่ารัก แต่ผิดUNION ALLวิธีแก้ปัญหา นอกจากนี้ยังนำเสนอโซลูชันที่ใช้UNIONซึ่งจะช้าลงในตารางแหล่งที่มาขนาดใหญ่เนื่องจากการทำซ้ำที่จำเป็น ในที่สุดก็จะไม่รวบรวมเพราะสนามไม่อยู่ในแบบสอบถามย่อยid tmp
Marc L.

ฉันไม่เคยอ้างสิทธิ์เกี่ยวกับความเร็วเลยและ OP ก็ไม่ได้พูดถึงความเร็วเลย สมมติว่ายูเนี่ยนทั้งหมด (คุณไม่ได้ระบุว่าอันไหน) และทั้งสองนี้ให้คำตอบที่ถูกต้องถ้าเราต้องการยืนยันว่าอันนั้นเร็วกว่าเราจะต้องให้การวัดประสิทธิภาพและนั่นจะเป็นการยั่วยุจาก OP คำถาม.
Angelos

สำหรับการสังเกตเกี่ยวกับ id ที่ไม่ได้อยู่ในคิวรีย่อยฉันได้แก้ไขตัวพิมพ์ผิด - ขอบคุณสำหรับการชี้ให้เห็น การอ้างสิทธิ์ผิด ๆ ของคุณนั้นคลุมเครือ - หากคุณอาจให้ข้อมูลเพิ่มเติมฉันสามารถจัดการเรื่องนั้นได้ ในการสังเกตครั้งสุดท้ายของคุณเกี่ยวกับความน่ารักฉันไม่มีความคิดเห็นใด ๆ ฉันอยากจะเน้นไปที่ตรรกะของ sql
Angelos

3
ผิดพลาด: "การUNION ALLแก้ปัญหา: ... ยังไม่ถูกต้อง" รหัสที่คุณใบปัจจุบันออกจากสี่แยกยกเว้นจากขวาเข้าร่วม ( where t1.id1 is null) UNION ALLที่จะต้องมีการระบุไว้ใน กล่าวคือโซลูชันของคุณสำคัญกว่าโซลูชันอื่นทั้งหมดเฉพาะเมื่อโซลูชันอื่น ๆ เหล่านั้นถูกนำไปใช้อย่างไม่ถูกต้อง ที่จุด "น่ารัก" นั่นคือฟรีฉันขอโทษ
Marc L.

-3

มาตรฐาน SQL บอกว่าfull join onเป็นinner join onแถวที่union allไม่มีใครเทียบแถวซ้ายของตารางที่ขยายโดย nulls union allแถวขวาของตารางที่ขยายโดย nulls เช่นinner join onแถวunion allแถวในleft join onแต่ไม่inner join on union allแถวในแต่ไม่right join oninner join on

เช่นleft join onแถวแถวไม่ได้อยู่ในunion all right join on inner join onหรือถ้าคุณรู้ว่าinner join onผลลัพธ์ของคุณไม่สามารถมีค่าว่างในคอลัมน์ตารางด้านขวาเฉพาะ " right join onแถวที่ไม่ได้อยู่ในinner join on" คือแถวที่right join onมีonเงื่อนไขที่ขยายโดยandคอลัมน์is nullนั้น

เช่นแถวright join on union allที่เหมาะสมในทำนองเดียวกันleft join on

จากความแตกต่างระหว่าง“INNER JOIN” และ“OUTER JOIN” คืออะไร? :

(SQL Standard 2006 SQL / Foundation 7.7 กฎไวยากรณ์ 1, กฎทั่วไป 1 b, 3 c & d, 5 b.)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.