SQL JOIN และ JOIN ชนิดต่าง ๆ


คำตอบ:


330

ภาพประกอบจากW3schools :


เข้าร่วมภายใน - บันทึกเฉพาะที่ตรงกับเงื่อนไขในตารางทั้งสอง


ซ้ายเข้าร่วม - บันทึกทั้งหมดจากตารางที่ 1 ร่วมกับบันทึกที่ตรงกับเงื่อนไขในตารางที่ 2


เข้าร่วมขวา - ระเบียนทั้งหมดจากตารางที่ 2 ร่วมกับระเบียนจากตารางที่ 1 ซึ่งตรงกับเงื่อนไข


FULL OUTER JOIN - การรวมกันของทั้งด้านซ้ายและด้านนอกเข้าร่วมการจับคู่กับอนุประโยค แต่รักษาทั้งสองตาราง



27
@KNU เครื่องมือ w3fools ควรให้เครดิตจากที่ที่พวกเขาได้นำแนวคิดมาใช้กับรูปภาพ ดูการสร้างภาพข้อมูล SQL เข้าร่วมโดย Jeff Atwood (ใช่ผู้ร่วมเขียน SO) และบทความที่เชื่อมโยงโดย Ligaya Turmelleที่ Jeff มีความคิดและอธิบาย
ypercubeᵀᴹ

2
@avi การรวมซ้ายและขวาคล้ายกันหากคุณไม่ใส่ใจซึ่งเป็นตารางหลักการเข้าร่วมจะขึ้นอยู่กับ
Anup

2
@philipxy: นั่นเป็นคำนิยามที่แปลก (แม้ว่าคุณจะถูกต้อง) แต่ฉันอยากจะไปอีกทางแล้วเริ่มต้นด้วยการเข้าร่วมแบบครอสและจากนั้น "สร้าง" การรวมภายในด้านบนของมัน ท้ายที่สุดแนวคิดของการเข้าร่วมไขว้จะทำให้การแสดงภาพแผนภาพเวนน์ไม่เป็นทางการและไม่ถูกต้องเหล่านี้ ...
ลูคัสเอเดอร์

1
ภาพเหล่านี้ดูเหมือนจะบ่งบอกว่าการรวมกันเป็นเหมือนการเข้าร่วมเต็มรูปแบบด้านนอกและจุดตัดเหมือนกับการเข้าร่วมภายในซึ่งไม่ถูกต้องเท่าที่ฉันรู้
mightyWOZ

1
@DevDave เพราะตรงกันข้ามกับความเชื่อที่นิยม - ภาพไม่คุ้มค่าพันคำ ดูคำตอบต่อไป
hyankov

248

คือSQL JOINอะไร

SQL JOIN เป็นวิธีการดึงข้อมูลจากสองตารางฐานข้อมูลหรือมากกว่านั้น

อะไรที่แตกต่างกันSQL JOIN?

มีทั้งหมดห้าJOINs พวกเขาเป็น :

  1. JOIN or INNER JOIN
  2. OUTER JOIN

     2.1 LEFT OUTER JOIN or LEFT JOIN
     2.2 RIGHT OUTER JOIN or RIGHT JOIN
     2.3 FULL OUTER JOIN or FULL JOIN

  3. NATURAL JOIN
  4. CROSS JOIN
  5. SELF JOIN

1. เข้าร่วมหรือเข้าร่วมภายใน:

ในประเภทนี้JOINเราจะได้รับรายงานทั้งหมดที่ตรงกับเงื่อนไขในทั้งสองตารางและไม่มีการรายงานระเบียนในทั้งสองตารางที่ไม่ตรงกัน

กล่าวอีกนัยหนึ่งINNER JOINจะขึ้นอยู่กับข้อเท็จจริงเดียวที่: เฉพาะรายการที่ตรงกันในทั้งสองตารางควรแสดงรายการ

ทราบว่าJOINโดยไม่ต้องอื่น ๆJOINคำหลัก (เช่นINNER, OUTER, LEFTฯลฯ ) INNER JOINเป็น กล่าวอีกนัยหนึ่งJOINคือน้ำตาลซินแทคติกสำหรับINNER JOIN(ดู: ความแตกต่างระหว่างเข้าร่วมและเข้าร่วมภายใน )

2. เข้าร่วมนอก:

OUTER JOIN ดึง

ไม่ว่าจะเป็นแถวที่ตรงกันจากตารางหนึ่งและแถวทั้งหมดในตารางอื่นหรือแถวทั้งหมดในตารางทั้งหมด (ไม่สำคัญว่าจะมีการจับคู่หรือไม่ก็ตาม)

การเข้าร่วม Outer มีสามชนิด:

2.1 ซ้ายซ้ายเข้าร่วมหรือซ้ายเข้าร่วม

การเข้าร่วมนี้จะส่งคืนแถวทั้งหมดจากตารางด้านซ้ายพร้อมกับแถวที่ตรงกันจากตารางด้านขวา หากไม่มีคอลัมน์ที่ตรงกับในตารางด้านขวามันจะส่งคืนNULLค่า

2.2 เข้าร่วมด้านขวาหรือเข้าร่วมขวา

สิ่งนี้JOINจะคืนค่าแถวทั้งหมดจากตารางด้านขวาพร้อมกับแถวที่ตรงกันจากตารางด้านซ้าย หากไม่มีคอลัมน์ที่ตรงกับในตารางด้านซ้ายมันจะคืนNULLค่า

2.3 เต็มเต็มเข้าร่วมหรือเต็มเข้าร่วม

นี้JOINรวมและLEFT OUTER JOIN RIGHT OUTER JOINมันจะส่งคืนแถวจากทั้งสองตารางเมื่อตรงตามเงื่อนไขและคืนNULLค่าเมื่อไม่มีการจับคู่

กล่าวอีกนัยหนึ่งOUTER JOINขึ้นอยู่กับความจริงที่ว่า: เฉพาะรายการที่ตรงกันในหนึ่งในตาราง (ขวาหรือซ้าย) หรือทั้งสองของตาราง (เต็ม) ควรแสดงรายการ

Note that `OUTER JOIN` is a loosened form of `INNER JOIN`.

3. เข้าร่วมธรรมชาติ:

มันขึ้นอยู่กับเงื่อนไขสองประการ:

  1. JOINจะทำบนคอลัมน์ทั้งหมดที่มีชื่อเดียวกันเพื่อความเท่าเทียมกัน
  2. ลบคอลัมน์ที่ซ้ำกันออกจากผลลัพธ์

สิ่งนี้ดูเหมือนจะเป็นทฤษฎีมากกว่าปกติและเป็นผล (อาจ) DBMS ส่วนใหญ่ไม่สนใจที่จะสนับสนุนสิ่งนี้

4. CROSS เข้าร่วม:

มันเป็นผลิตภัณฑ์คาร์ทีเซียนของทั้งสองตารางที่เกี่ยวข้อง ผลลัพธ์ของ a CROSS JOINจะไม่สมเหตุสมผลในสถานการณ์ส่วนใหญ่ ยิ่งกว่านั้นเราไม่ต้องการสิ่งนี้เลย (หรือต้องการอย่างน้อยก็แม่นยำ)

5. เข้าร่วมด้วยตนเอง:

มันไม่ได้เป็นรูปแบบที่แตกต่างกันJOINค่อนข้างจะเป็นJOIN( INNER, OUTERฯลฯ ) ของตารางกับตัวเอง

เข้าร่วมขึ้นอยู่กับผู้ประกอบการ

ทั้งนี้ขึ้นอยู่กับผู้ประกอบการที่ใช้สำหรับJOINข้อสามารถมีได้ทั้งสองประเภทของJOINs พวกเขาเป็น

  1. เข้าร่วม Equi
  2. เข้าร่วม Theta

1. เข้าร่วม Equi:

สำหรับสิ่งที่JOINประเภท ( INNER, OUTERฯลฯ ) ถ้าเราใช้เฉพาะผู้ประกอบการความเท่าเทียมกัน (=) แล้วเราบอกว่าเป็นJOINEQUI JOIN

2. เข้าร่วม Theta:

สิ่งนี้เหมือนกับEQUI JOINแต่อนุญาตให้ตัวดำเนินการอื่นทั้งหมดเช่น>, <,> = เป็นต้น

หลายคนคิดว่าทั้งสองEQUI JOINและทีJOINคล้ายกับINNER, OUTER ฯลฯJOINs แต่ฉันเชื่ออย่างยิ่งว่ามันเป็นความผิดพลาดและทำให้ความคิดที่คลุมเครือ เพราะINNER JOIN, OUTER JOINฯลฯ มีการเชื่อมต่อทั้งหมดที่มีตารางและข้อมูลของพวกเขาในขณะที่EQUI JOINและTHETA JOINมีการเชื่อมต่อเฉพาะกับผู้ประกอบการที่เราใช้ในอดีต

อีกครั้งมีหลายคนที่พิจารณาNATURAL JOINเป็น EQUI JOIN"แปลกประหลาด" NATURAL JOINในความเป็นจริงมันเป็นความจริงเพราะของสภาพแรกที่ผมกล่าวถึง อย่างไรก็ตามเราไม่จำเป็นต้อง จำกัด เพียงNATURAL JOINอย่างเดียว INNER JOINs, OUTER JOINฯลฯ อาจเป็นEQUI JOINเช่นกัน


2
มีการเข้าร่วมใน LATERAL ที่ค่อนข้างใหม่ .. เลือก * จาก r1, LATERAL fx (r1)
Pavel Stehule

13
แม้ว่าจะดูสมเหตุสมผล แต่ฉันไม่คิดว่าคำตอบ "การเข้าร่วม SQL คืออะไร" ในทุก ๆ ทางที่สื่อข้อมูลที่เป็นประโยชน์ คำตอบโดยรวมเป็นข้อมูลอ้างอิงที่เขียนขึ้นสำหรับผู้ที่เข้าใจแล้วเข้าร่วมไม่ใช่สำหรับคนที่ถามคำถามเหล่านั้น นอกจากนี้ยังละเว้นการอ้างอิงทั้งเพื่อสนับสนุนการอ้างสิทธิ์ (ตามความเหมาะสมหากทำคำตอบโดยอนุญาต) และให้คำอธิบายเพิ่มเติมผ่านแหล่งข้อมูลภายนอก หากคุณกำลังพยายามที่จะเขียนคำตอบที่มีสิทธิ์ในการเชื่อมโยงผู้ใช้ SQL ใหม่ไปยังมันอาจจะมีมูลค่าเติมในช่องว่างเล็กน้อยโดยเฉพาะอย่างยิ่งส่วน "สิ่งที่เข้าร่วม"
Craig Ringer

คุณสามารถให้ตัวอย่างได้ไหม
avi

67

ความหมาย:


เข้าร่วมเป็นวิธีการค้นหาข้อมูลที่รวมเข้าด้วยกันจากหลายตารางพร้อมกัน

ประเภทของการเข้าร่วม:


เกี่ยวกับ RDBMS มีการรวม 5 ประเภท:

  • เข้าร่วม Equi:รวมระเบียนทั่วไปจากสองตารางตามเงื่อนไขความเท่าเทียมกัน ในทางเทคนิคการเข้าร่วมทำได้โดยใช้ตัวดำเนินการเท่าเทียมกัน (=) เพื่อเปรียบเทียบค่าของคีย์หลักของตารางหนึ่งและค่าคีย์ต่างประเทศของตารางอื่นดังนั้นชุดผลลัพธ์จึงรวมเร็กคอร์ด (จับคู่) ทั่วไปจากทั้งสองตาราง สำหรับการติดตั้งโปรดดูที่ INNER-JOIN

  • Natural-Join: เป็นรุ่นที่ได้รับการปรับปรุงของ Equi-Join ซึ่งการดำเนินการ SELECT จะไม่แสดงคอลัมน์ที่ซ้ำกัน สำหรับการติดตั้งโปรดดูที่ INNER-JOIN

  • Non-Equi-Join:เป็นการย้อนกลับของการเข้าร่วม Equi โดยที่เงื่อนไขการเข้าร่วมถูกใช้นอกเหนือจากตัวดำเนินการเท่ากัน (=) เช่น,! =, <=,> =,>, <หรือระหว่าง ฯลฯ สำหรับการนำไปใช้ให้ดู INNER-JOIN

  • เข้าร่วมตัวเอง::พฤติกรรมที่กำหนดเองของการเข้าร่วมที่ตารางรวมกับตัวเอง; โดยทั่วไปสิ่งนี้จำเป็นสำหรับการสอบถามตารางการอ้างอิงตนเอง (หรือเอนทิตีความสัมพันธ์ของ Unary) สำหรับการนำไปใช้ให้ดูที่ INNER-JOIN

  • ผลิตภัณฑ์คาร์ทีเซียน:มันรวมบันทึกทั้งหมดของตารางทั้งสองโดยไม่มีเงื่อนไขใด ๆ ในทางเทคนิคแล้วมันจะส่งคืนชุดผลลัพธ์ของแบบสอบถามโดยไม่มี WHERE-Clause

ตามความกังวลและความก้าวหน้าของ SQL มีการรวม 3 แบบและการรวม RDBMS ทั้งหมดสามารถทำได้โดยใช้การรวมประเภทนี้

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

  2. OUTER-JOIN:เป็นการผสาน (หรือรวม) การจับคู่แถวจากสองตารางและแถวที่ไม่ตรงกันด้วยค่า NULL อย่างไรก็ตามสามารถปรับแต่งการเลือกแถวที่ไม่ตรงกันเช่นการเลือกแถวที่ไม่ตรงกันจากตารางแรกหรือตารางที่สองตามประเภทย่อย: LEFT OUTER JOIN และ RIGHT OUTER JOIN

    2.1 LEFT Outer JOIN (aka, LEFT-JOIN): ส่งคืนแถวที่ตรงกันจากสองตารางและไม่ตรงกันจากตาราง LEFT (เช่นตารางแรก) เท่านั้น

    2.2 RIGHT Outer JOIN (aka, RIGHT-JOIN): ส่งคืนแถวที่ตรงกันจากสองตารางและไม่ตรงกันจากตาราง RIGHT เท่านั้น

    2.3 FULL OUTER JOIN (aka OUTER JOIN): ส่งคืนการจับคู่และไม่ตรงกันจากทั้งสองตาราง

  3. CROSS-JOIN: การเข้าร่วมนี้ไม่ได้รวม / รวมเข้าด้วยกัน แต่จะทำงานร่วมกับผลิตภัณฑ์คาร์ทีเซียน

ป้อนคำอธิบายรูปภาพที่นี่ หมายเหตุ: การเข้าร่วมด้วยตนเองสามารถทำได้โดยใช้ INNER-JOIN, OUTER-JOIN และ CROSS-JOIN ตามความต้องการ แต่ตารางจะต้องเข้าร่วมกับตัวเอง

สำหรับข้อมูลเพิ่มเติม:

ตัวอย่าง:

1.1: INNER-JOIN: การติดตั้ง Equi-join

SELECT  *
FROM Table1 A 
 INNER JOIN Table2 B ON A.<Primary-Key> =B.<Foreign-Key>;

1.2: ภายใน -JININ: การดำเนินการตามธรรมชาติเข้าร่วม

Select A.*, B.Col1, B.Col2          --But no B.ForeignKeyColumn in Select
 FROM Table1 A
 INNER JOIN Table2 B On A.Pk = B.Fk;

1.3: ภายในเข้าร่วมกับการใช้งานแบบไม่เข้าร่วม Equi

Select *
 FROM Table1 A INNER JOIN Table2 B On A.Pk <= B.Fk;

1.4: ภายในเข้าร่วมด้วยตนเองเข้าร่วม

Select *
 FROM Table1 A1 INNER JOIN Table1 A2 On A1.Pk = A2.Fk;

2.1: นอกเข้าร่วม (เต็มภายนอกเข้าร่วม)

Select *
 FROM Table1 A FULL OUTER JOIN Table2 B On A.Pk = B.Fk;

2.2: ซ้ายเข้าร่วม

Select *
 FROM Table1 A LEFT OUTER JOIN Table2 B On A.Pk = B.Fk;

2.3: เข้าร่วมทางขวา

Select *
 FROM Table1 A RIGHT OUTER JOIN Table2 B On A.Pk = B.Fk;

3.1: CROSS JOIN

Select *
 FROM TableA CROSS JOIN TableB;

3.2: CROSS เข้าร่วมด้วยตนเองเข้าร่วม

Select *
 FROM Table1 A1 CROSS JOIN Table1 A2;

//หรือ//

Select *
 FROM Table1 A1,Table1 A2;

ป้ายกำกับ "ตารางที่ 1" และ "ตารางที่ 2" และป้ายกำกับที่อยู่ด้านล่างไม่เหมาะสมฉลากเหล่านั้นมาจากภาพประกอบของintersect/ except/ union; ที่นี่วงกลมคือแถวที่ส่งกลับโดยleft& right joinตามที่ป้ายกำกับหมายเลขระบุว่า ภาพ AXB ไร้สาระ cross join= inner join on 1=1& เป็นกรณีพิเศษของแผนภาพแรก
philipxy

มันมูลค่าการกล่าวขวัญ SQL-92 UNION JOINกำหนด ตอนนี้ล้าสมัยแล้วใน SQL: 2003
Impaler

40

คำตอบอื่น ๆ ที่น่าสนใจที่สุดประสบกับปัญหาทั้งสองนี้:

เมื่อเร็ว ๆ นี้ฉันได้เขียนบทความเกี่ยวกับหัวข้อ: คู่มือที่ครอบคลุมไม่สมบูรณ์สำหรับวิธีการเข้าร่วมตารางใน SQL ที่หลากหลายซึ่งอาจสรุปได้ที่นี่

สิ่งแรกและสำคัญที่สุด: การเข้าร่วมเป็นผลิตภัณฑ์คาร์ทีเซียน

นี่คือเหตุผลที่ไดอะแกรมของเวนน์อธิบายไม่ถูกต้องเนื่องจากการเข้าร่วมสร้างผลิตภัณฑ์คาร์ทีเซียนระหว่างสองตารางที่เข้าร่วม Wikipedia แสดงให้เห็นเป็นอย่างดี:

ป้อนคำอธิบายรูปภาพที่นี่

ไวยากรณ์ SQL CROSS JOINสำหรับผลิตภัณฑ์คาร์ทีเซียนคือ ตัวอย่างเช่น:

SELECT *

-- This just generates all the days in January 2017
FROM generate_series(
  '2017-01-01'::TIMESTAMP,
  '2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
  INTERVAL '1 day'
) AS days(day)

-- Here, we're combining all days with all departments
CROSS JOIN departments

ซึ่งรวมแถวทั้งหมดจากตารางหนึ่งกับแถวทั้งหมดจากตารางอื่น:

ที่มา:

+--------+   +------------+
| day    |   | department |
+--------+   +------------+
| Jan 01 |   | Dept 1     |
| Jan 02 |   | Dept 2     |
| ...    |   | Dept 3     |
| Jan 30 |   +------------+
| Jan 31 |
+--------+

ผลลัพธ์:

+--------+------------+
| day    | department |
+--------+------------+
| Jan 01 | Dept 1     |
| Jan 01 | Dept 2     |
| Jan 01 | Dept 3     |
| Jan 02 | Dept 1     |
| Jan 02 | Dept 2     |
| Jan 02 | Dept 3     |
| ...    | ...        |
| Jan 31 | Dept 1     |
| Jan 31 | Dept 2     |
| Jan 31 | Dept 3     |
+--------+------------+

หากเราเพิ่งเขียนรายการตารางคั่นด้วยเครื่องหมายจุลภาคเราจะได้รับเหมือนกัน:

-- CROSS JOINing two tables:
SELECT * FROM table1, table2

เข้าร่วมภายใน (Theta-JOIN)

An INNER JOINเป็นเพียงตัวกรองCROSS JOINที่เรียกว่าตัวกรองคำThetaในพีชคณิตเชิงสัมพันธ์

ตัวอย่างเช่น

SELECT *

-- Same as before
FROM generate_series(
  '2017-01-01'::TIMESTAMP,
  '2017-01-01'::TIMESTAMP + INTERVAL '1 month -1 day',
  INTERVAL '1 day'
) AS days(day)

-- Now, exclude all days/departments combinations for
-- days before the department was created
JOIN departments AS d ON day >= d.created_at

โปรดทราบว่าคำหลักINNERนั้นเป็นตัวเลือก (ยกเว้นใน MS Access)

( ดูบทความสำหรับตัวอย่างผลลัพธ์ )

เข้าร่วม EQUI

Theta-JOIN ชนิดพิเศษคือ equi JOIN ซึ่งเราใช้มากที่สุด เพรดิเคตจะรวมคีย์หลักของตารางหนึ่งโดยมี foreign key ของอีกตารางหนึ่ง ถ้าเราใช้ฐานข้อมูล Sakilaเป็นภาพประกอบเราสามารถเขียน:

SELECT *
FROM actor AS a
JOIN film_actor AS fa ON a.actor_id = fa.actor_id
JOIN film AS f ON f.film_id = fa.film_id

รวมนักแสดงทั้งหมดเข้ากับภาพยนตร์ของพวกเขา

หรือในบางฐานข้อมูล:

SELECT *
FROM actor
JOIN film_actor USING (actor_id)
JOIN film USING (film_id)

USING()ไวยากรณ์ช่วยให้การระบุคอลัมน์ที่ต้องมีอยู่บนด้านข้างของทั้ง JOIN ตารางการดำเนินงานและสร้างความเท่าเทียมกันในกริยาทั้งสองคอลัมน์

เข้าร่วมธรรมชาติ

คำตอบอื่น ๆ มีการระบุไว้ "ประเภท JOIN" นี้แยกจากกัน แต่นั่นก็ไม่สมเหตุสมผล มันเป็นเพียงรูปแบบซินแทกซ์สำหรับ equi JOIN ซึ่งเป็นกรณีพิเศษของ Theta-JOIN หรือ INNER JOIN NAT JOIN JOIN รวบรวมคอลัมน์ทั้งหมดที่ใช้ร่วมกับตารางทั้งสองเข้าร่วมและรวมUSING()คอลัมน์เหล่านั้นเข้าด้วยกัน ซึ่งไม่ค่อยมีประโยชน์เนื่องจากการจับคู่โดยไม่ตั้งใจ (เช่นLAST_UPDATEคอลัมน์ในฐานข้อมูล Sakila )

นี่คือไวยากรณ์:

SELECT *
FROM actor
NATURAL JOIN film_actor
NATURAL JOIN film

เข้าร่วมด้านนอก

ตอนนี้OUTER JOINมันแตกต่างจากINNER JOINที่มันสร้างUNIONผลิตภัณฑ์คาร์ทีเซียนหลายอย่าง เราสามารถเขียน:

-- Convenient syntax:
SELECT *
FROM a LEFT JOIN b ON <predicate>

-- Cumbersome, equivalent syntax:
SELECT a.*, b.*
FROM a JOIN b ON <predicate>
UNION ALL
SELECT a.*, NULL, NULL, ..., NULL
FROM a
WHERE NOT EXISTS (
  SELECT * FROM b WHERE <predicate>
)

ไม่มีใครต้องการที่จะเขียนหลังดังนั้นเราเขียนOUTER JOIN(ซึ่งมักจะดีที่สุดโดยฐานข้อมูล)

เช่นเดียวINNERกับคำหลักOUTERเป็นตัวเลือกที่นี่

OUTER JOIN มาในสามรสชาติ:

  • LEFT [ OUTER ] JOIN: ตารางด้านซ้ายของJOINนิพจน์ถูกเพิ่มเข้ากับสหภาพดังที่แสดงไว้ด้านบน
  • RIGHT [ OUTER ] JOIN: ตารางด้านขวาของJOINนิพจน์ถูกเพิ่มเข้าไปในสหภาพดังที่แสดงไว้ด้านบน
  • FULL [ OUTER ] JOIN: ทั้งสองตารางของJOINนิพจน์จะถูกเพิ่มเข้าไปในยูเนี่ยน

สิ่งเหล่านี้สามารถนำมารวมกับคำหลักUSING()หรือกับNATURAL( ฉันมีกรณีการใช้งานจริงสำหรับNATURAL FULL JOINเมื่อเร็ว ๆ นี้ )

ไวยากรณ์ทางเลือก

มีบางประวัติศาสตร์ที่ไม่สนับสนุนไวยากรณ์ใน Oracle และ SQL Server ซึ่งได้รับการสนับสนุนOUTER JOINแล้วก่อนที่มาตรฐาน SQL มีไวยากรณ์สำหรับสิ่งนี้:

-- Oracle
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id = fa.actor_id(+)
AND fa.film_id = f.film_id(+)

-- SQL Server
SELECT *
FROM actor a, film_actor fa, film f
WHERE a.actor_id *= fa.actor_id
AND fa.film_id *= f.film_id

ต้องบอกว่าอย่าใช้ไวยากรณ์นี้ ฉันเพิ่งแสดงรายการที่นี่เพื่อให้คุณสามารถจดจำได้จากการโพสต์บล็อกเก่า / รหัสเดิม

แบ่งพาร์ติชัน OUTER JOIN

มีคนเพียงไม่กี่คนที่รู้สิ่งนี้ แต่มาตรฐาน SQL ระบุการแบ่งพาร์ติชันOUTER JOIN(และ Oracle ดำเนินการ) คุณสามารถเขียนสิ่งนี้:

WITH

  -- Using CONNECT BY to generate all dates in January
  days(day) AS (
    SELECT DATE '2017-01-01' + LEVEL - 1
    FROM dual
    CONNECT BY LEVEL <= 31
  ),

  -- Our departments
  departments(department, created_at) AS (
    SELECT 'Dept 1', DATE '2017-01-10' FROM dual UNION ALL
    SELECT 'Dept 2', DATE '2017-01-11' FROM dual UNION ALL
    SELECT 'Dept 3', DATE '2017-01-12' FROM dual UNION ALL
    SELECT 'Dept 4', DATE '2017-04-01' FROM dual UNION ALL
    SELECT 'Dept 5', DATE '2017-04-02' FROM dual
  )
SELECT *
FROM days 
LEFT JOIN departments 
  PARTITION BY (department) -- This is where the magic happens
  ON day >= created_at

ส่วนของผลลัพธ์:

+--------+------------+------------+
| day    | department | created_at |
+--------+------------+------------+
| Jan 01 | Dept 1     |            | -- Didn't match, but still get row
| Jan 02 | Dept 1     |            | -- Didn't match, but still get row
| ...    | Dept 1     |            | -- Didn't match, but still get row
| Jan 09 | Dept 1     |            | -- Didn't match, but still get row
| Jan 10 | Dept 1     | Jan 10     | -- Matches, so get join result
| Jan 11 | Dept 1     | Jan 10     | -- Matches, so get join result
| Jan 12 | Dept 1     | Jan 10     | -- Matches, so get join result
| ...    | Dept 1     | Jan 10     | -- Matches, so get join result
| Jan 31 | Dept 1     | Jan 10     | -- Matches, so get join result

ประเด็นคือที่นี่แถวทั้งหมดจากด้านพาร์ติชันของการเข้าร่วมจะสิ้นสุดในผลลัพธ์โดยไม่คำนึงว่าJOINอะไรที่ตรงกันใน "ด้านอื่น ๆ ของการเข้าร่วม" สั้นเรื่องสั้น: นี่คือการเติมข้อมูลเบาบางในรายงาน มีประโยชน์มาก!

เข้าร่วม SEMI

อย่างจริงจัง? ไม่มีคำตอบอื่น ๆ ที่ได้รับนี้? ไม่แน่นอนเนื่องจากไม่มีไวยากรณ์ดั้งเดิมใน SQL แต่น่าเสียดายที่ (เหมือน ANTI JOIN ด้านล่าง) แต่เราสามารถใช้IN()และEXISTS()เช่นเพื่อค้นหานักแสดงทุกคนที่เล่นในภาพยนตร์:

SELECT *
FROM actor a
WHERE EXISTS (
  SELECT * FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)

เพรดิเคตWHERE a.actor_id = fa.actor_idทำหน้าที่เป็นเพรดิเคตเข้าร่วมกึ่ง หากคุณไม่เชื่อให้ลองใช้แผนการดำเนินการเช่นใน Oracle คุณจะเห็นว่าฐานข้อมูลรันการดำเนินการ SEMI JOIN ไม่ใช่EXISTS()คำกริยา

ป้อนคำอธิบายรูปภาพที่นี่

เข้าร่วม ANTI

นี่เป็นเพียงตรงข้ามของกึ่ง JOIN ( ต้องระวังไม่ให้ใช้NOT INแต่เป็นมันมีข้อแม้สำคัญ)

นี่คือนักแสดงทุกคนที่ไม่มีภาพยนตร์:

SELECT *
FROM actor a
WHERE NOT EXISTS (
  SELECT * FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)

บางคน (โดยเฉพาะคน MySQL) ก็เขียน ANTI JOIN เช่นนี้:

SELECT *
FROM actor a
LEFT JOIN film_actor fa
USING (actor_id)
WHERE film_id IS NULL

ฉันคิดว่าเหตุผลทางประวัติศาสตร์คือการแสดง

เข้าร่วมในช่วงท้าย

OMG อันนี้เจ๋งเกินไป ฉันเป็นคนเดียวที่พูดถึงมัน? นี่คือแบบสอบถามที่ยอดเยี่ยม:

SELECT a.first_name, a.last_name, f.*
FROM actor AS a
LEFT OUTER JOIN LATERAL (
  SELECT f.title, SUM(amount) AS revenue
  FROM film AS f
  JOIN film_actor AS fa USING (film_id)
  JOIN inventory AS i USING (film_id)
  JOIN rental AS r USING (inventory_id)
  JOIN payment AS p USING (rental_id)
  WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
  GROUP BY f.film_id
  ORDER BY revenue DESC
  LIMIT 5
) AS f
ON true

จะพบกับภาพยนตร์ที่สร้างรายได้สูงสุด 5 อันดับแรกต่อนักแสดง ทุกครั้งที่คุณต้องการแบบสอบถาม TOP-N-per-something LATERAL JOINจะเป็นเพื่อนของคุณ หากคุณเป็นบุคคล SQL Server คุณจะรู้JOINประเภทนี้ภายใต้ชื่อAPPLY

SELECT a.first_name, a.last_name, f.*
FROM actor AS a
OUTER APPLY (
  SELECT f.title, SUM(amount) AS revenue
  FROM film AS f
  JOIN film_actor AS fa ON f.film_id = fa.film_id
  JOIN inventory AS i ON f.film_id = i.film_id
  JOIN rental AS r ON i.inventory_id = r.inventory_id
  JOIN payment AS p ON r.rental_id = p.rental_id
  WHERE fa.actor_id = a.actor_id -- JOIN predicate with the outer query!
  GROUP BY f.film_id
  ORDER BY revenue DESC
  LIMIT 5
) AS f

ตกลงบางทีการโกงเพราะLATERAL JOINหรือAPPLYการแสดงออกเป็นจริง "แบบสอบถามย่อยสัมพันธ์" ที่ผลิตหลายแถว แต่ถ้าเราอนุญาตให้ "เคียวรีย่อยที่สัมพันธ์กัน" เราสามารถพูดคุยเกี่ยวกับ ...

MultiSet

สิ่งนี้นำมาใช้จริงโดย Oracle และ Informix (ตามความรู้ของฉัน) แต่สามารถจำลองใน PostgreSQL โดยใช้อาร์เรย์และ / หรือ XML และใน SQL Server โดยใช้ XML

MULTISETสร้างแบบสอบถามย่อยที่มีความสัมพันธ์และสร้างชุดผลลัพธ์ของแถวในแบบสอบถามภายนอก แบบสอบถามด้านล่างเลือกนักแสดงทุกคนและสำหรับนักแสดงแต่ละคนรวบรวมภาพยนตร์ของพวกเขาในคอลเลกชันซ้อนกัน:

SELECT a.*, MULTISET (
  SELECT f.*
  FROM film AS f
  JOIN film_actor AS fa USING (film_id)
  WHERE a.actor_id = fa.actor_id
) AS films
FROM actor

ในขณะที่คุณได้เห็นมีหลายประเภทมากขึ้นของการเข้าร่วมมากกว่าเพียงแค่ "น่าเบื่อ" INNER, OUTERและCROSS JOINที่มักจะกล่าวถึง รายละเอียดเพิ่มเติมในบทความของฉัน และโปรดหยุดใช้ไดอะแกรมของเวนน์เพื่ออธิบาย


Equijoin เป็นกรณีพิเศษของการเข้าร่วม theta โดยทีต้ามีความเท่าเทียมกัน Theta-join นั้นคล้ายคลึงกับกรณีพิเศษของการรวมภายในโดยที่ on เป็นการเปรียบเทียบ theta ของคอลัมน์จากแต่ละคอลัมน์ หลายสิบปีหลังจากที่ Codd ได้กำหนดตำราเรียนของพวกเขาบางอย่างผิดพลาดทีต้าเข้าร่วมเป็นลักษณะทั่วไปที่เป็นอนาล็อกของการรวมภายใน
philipxy

@philipxy: มีอะไรพิเศษที่ฉันควรเปลี่ยนในคำตอบของฉัน? คุณสามารถแนะนำการแก้ไข ...
Lukas Eder

10

ฉันสร้างภาพประกอบที่อธิบายได้ดีกว่าคำพูดในความคิดของฉัน: ตารางคำอธิบาย SQL Join


@Niraj แวดวง A & B ไม่มีแถวของ A & B พวกเขาถูกคัดลอกมาจากที่อื่นโดยไม่มีเครดิต การเข้าร่วมข้ามรวมอยู่ในกรณีการรวมภายในมันเป็นการรวมภายในเมื่อ 1 = 1 ส่วนเหล่านี้ของภาพ "สมบูรณ์แบบ" ในลักษณะใด
philipxy

@philipxy ขออภัย แต่ฉันไม่ใส่ใจถ้ามันถูกคัดลอกมาจากที่อื่น และฉันไม่แน่ใจว่าสิ่งใดที่ไม่ถูกต้องในภาพด้านบน สำหรับฉันมันก็โอเค การเข้าร่วมไขว้ไม่ได้อธิบายไว้ที่นี่ ไม่รวมอยู่ในการเข้าร่วมภายใน ..
Niraj

-3

ฉันจะผลักดันสัตว์เลี้ยงฉุนเฉียวของฉัน: คำหลักที่ใช้

หากทั้งสองตารางในทั้งสองด้านของ JOIN มีชื่อต่างประเทศที่ถูกต้อง (เช่นชื่อเดียวกันไม่ใช่แค่ "id) คุณสามารถใช้สิ่งนี้ได้:

SELECT ...
FROM customers JOIN orders USING (customer_id)

ฉันพบว่ามันใช้งานได้ดีอ่านได้และไม่ได้ใช้บ่อยเพียงพอ


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