ข้อจำกัดของการเชื่อมตารางกับตัวเอง
กรณีที่ควรใช้การเรียกซ้ำแทน
ข้อจำกัดของการเชื่อมตารางกับตัวเอง เป็นบทเรียน SQL Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL Academy มีบทเรียนทั้งหมด 4 บทเรียน
การเชื่อมตารางกับตัวเองคืออะไร
การเชื่อมตารางกับตัวเอง คือการนำตารางมาเชื่อมกับตัวมันเอง วิธีนี้มีประโยชน์สำหรับการเปรียบเทียบแถวภายในตารางเดียวกัน เช่น การค้นหาพนักงานและผู้จัดการของพวกเขาที่จัดเก็บอยู่ในตาราง employees เดียวกัน
ก่อนสำรวจข้อจำกัด เรามาทบทวนกันว่าการเชื่อมตารางกับตัวเองพื้นฐานทำงานจริงอย่างไร
SELECT e.name AS employee, m.name AS manager
FROM employees e
JOIN employees m ON e.manager_id = m.id;ลึกหนึ่งระดับ
การเชื่อมตารางกับตัวเองจัดการ หนึ่งขั้นในลำดับชั้นได้อย่างมีประสิทธิภาพ หากต้องการจับคู่พนักงานแต่ละคนกับผู้จัดการโดยตรง การเชื่อมตารางกับตัวเองเพียงครั้งเดียวก็เพียงพอ
วิธีนี้ทำงานได้อย่างสมบูรณ์เมื่อข้อมูลมีความลึกเพียงระดับเดียว หรือเมื่อคุณสนใจเฉพาะความสัมพันธ์ระหว่างโหนดแม่กับโหนดลูกโดยตรง
SELECT child.name AS employee, parent.name AS direct_manager
FROM employees child
LEFT JOIN employees parent ON child.manager_id = parent.id;สองระดับ: เริ่มซับซ้อนแล้ว
หากคุณต้องการพนักงาน ผู้จัดการของพวกเขา และผู้จัดการของผู้จัดการเหล่านั้นล่ะ คุณต้องเพิ่ม การเชื่อมตารางกับตัวเองครั้งที่สอง คิวรีจะยาวขึ้นและอ่านได้ยากกว่าเดิม
แต่ละระดับลำดับชั้นที่เพิ่มขึ้นต้องใช้ชื่อแทนการเชื่อมตารางเพิ่มอีกหนึ่งชื่อ และส่วนคำสั่ง JOIN เพิ่มอีกหนึ่งส่วน
SELECT e.name AS employee,
m.name AS manager,
gm.name AS grand_manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id
LEFT JOIN employees gm ON m.manager_id = gm.id;สามระดับ: รูปแบบเริ่มใช้ไม่ได้
การเพิ่มระดับที่สามทำให้ต้องเพิ่มการเชื่อมตารางอีกครั้ง เมื่อถึงจุดนี้คิวรีจะยืดยาว เปราะบาง และดูแลรักษาได้ยาก หากความลึกของลำดับชั้นเปลี่ยนแปลง คุณจะต้องเขียนคิวรีทั้งหมดใหม่
นี่คือ ข้อจำกัดสำคัญของการเชื่อมตารางกับตัวเอง ประการแรก: วิธีนี้ไม่รองรับลำดับชั้นที่มีความลึกเพิ่มขึ้น
SELECT e.name AS employee,
m.name AS manager,
gm.name AS grand_manager,
ggm.name AS great_grand_manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id
LEFT JOIN employees gm ON m.manager_id = gm.id
LEFT JOIN employees ggm ON gm.manager_id = ggm.id;ความลึกที่ไม่ทราบ: การเชื่อมตารางกับตัวเองช่วยไม่ได้
ในแผนผังองค์กรหรือต้นไม้หมวดหมู่ในโลกจริง ความลึกมักเป็นสิ่งที่ ไม่ทราบขณะคิวรี การเชื่อมตารางกับตัวเองบังคับให้คุณกำหนดจำนวนระดับไว้ตายตัว หากพรุ่งนี้ลำดับชั้นมีความลึก 10 ระดับ คิวรีที่เชื่อมตารางกับตัวเองไว้ 3 ระดับจะทำให้ข้อมูลตกหล่นโดยไม่แจ้งเตือน
นี่คือข้อจำกัดพื้นฐาน: การเชื่อมตารางกับตัวเองไม่สามารถไล่ผ่านระดับจำนวนเท่าใดก็ได้
-- This only retrieves up to 3 levels deep.
-- Employees deeper than level 3 are simply missing from results.
SELECT e.name, m.name, gm.name
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id
LEFT JOIN employees gm ON m.manager_id = gm.id;วงจรทำให้การเชื่อมตารางกับตัวเองใช้ไม่ได้โดยสิ้นเชิง
ข้อจำกัดร้ายแรงอีกประการหนึ่งคือ หากข้อมูลมี วงจร (A จัดการ B, B จัดการ C, C จัดการ A) คิวรีที่เชื่อมตารางกับตัวเองจะไม่วนซ้ำไม่สิ้นสุด แต่ก็ไม่สามารถตรวจจับหรือรายงานวงจรได้อย่างถูกต้อง
คุณไม่สามารถป้องกันการอ้างอิงแบบวนรอบด้วยการเชื่อมตารางกับตัวเองธรรมดาได้ คิวรีแบบเรียกซ้ำมีกลไกตรวจจับวงจรในตัว ซึ่งการเชื่อมตารางกับตัวเองไม่มีเลย
-- Cyclic data: row 3 points back to row 1
-- id | name | manager_id
-- 1 | Alice | 3 <-- cycle!
-- 2 | Bob | 1
-- 3 | Charlie | 2
-- A self join just shows one hop; it cannot detect the loop
SELECT e.name, m.name AS reports_to
FROM employees e
JOIN employees m ON e.manager_id = m.id;ทำความรู้จัก CTE แบบเรียกซ้ำ
ภาษาเอสคิวแอลมีวิธีแก้ปัญหาที่ออกแบบมาโดยเฉพาะสำหรับการไล่สำรวจลำดับชั้นที่มีความลึกไม่ทราบแน่ชัด นั่นคือ นิพจน์ตารางร่วมแบบเรียกซ้ำ (CTE) วิธีนี้ใช้รูปแบบไวยากรณ์ WITH RECURSIVE ซึ่ง PostgreSQL, MySQL 8+, เอสคิวไลต์ และเซิร์ฟเวอร์เอสคิวแอลรองรับ
CTE แบบเรียกซ้ำมีสองส่วน ได้แก่ สมาชิกจุดเริ่มต้น (แถวเริ่มต้น) และ สมาชิกแบบเรียกซ้ำ (ขั้นตอนที่ทำตามความสัมพันธ์แต่ละรายการ)
WITH RECURSIVE org_tree AS (
-- Anchor: start with the top-level CEO (no manager)
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- Recursive: find each employee whose manager is already in org_tree
SELECT e.id, e.name, e.manager_id, ot.depth + 1
FROM employees e
JOIN org_tree ot ON e.manager_id = ot.id
)
SELECT name, depth FROM org_tree ORDER BY depth;ติดตามเส้นทางทั้งหมด
คุณลักษณะที่ทรงพลังอย่างหนึ่งของ CTE แบบเรียกซ้ำคือ คุณสามารถ สะสมบริบท ขณะไล่ลงไปตามลำดับชั้นได้ ตัวอย่างเช่น คุณสามารถสร้างเส้นทางทั้งหมดจากรากไปยังแต่ละโหนด ซึ่งเป็นสิ่งที่การเชื่อมตารางกับตัวเองแบบตายตัวทำไม่ได้โดยสิ้นเชิง
WITH RECURSIVE org_tree AS (
SELECT id, name, manager_id,
name AS path
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id,
ot.path || ' > ' || e.name
FROM employees e
JOIN org_tree ot ON e.manager_id = ot.id
)
SELECT name, path FROM org_tree ORDER BY path;การเชื่อมตารางกับตัวเองเทียบกับ CTE แบบเรียกซ้ำ: ควรเลือกใช้เมื่อใด
ใช้ การเชื่อมตารางกับตัวเอง เมื่อ:
- คุณต้องการลำดับชั้นเพียงหนึ่งหรือสองระดับเท่านั้น
- ความลึกกำหนดตายตัวและทราบล่วงหน้า
- คุณต้องการความเรียบง่ายโดยไม่มีภาระเพิ่มจาก CTE
ใช้ CTE แบบเรียกซ้ำ เมื่อ:
- ความลึกเปลี่ยนแปลงได้หรือไม่ทราบแน่ชัด
- คุณต้องการเส้นทางสายบรรพบุรุษหรือลูกหลานทั้งหมด
- คุณต้องการตรวจจับวงจรผ่านส่วนคำสั่ง
CYCLEหรือใช้เงื่อนไขป้องกันด้วยตนเอง
ข้อควรพิจารณาด้านประสิทธิภาพ
การเชื่อมตารางกับตัวเองบนคอลัมน์ที่ทำดัชนีไว้ทำงานได้รวดเร็วมากสำหรับคิวรีที่มีความลึกตายตัว การเชื่อมตารางแต่ละครั้งเป็นการค้นหาเพียงครั้งเดียว และตัวเพิ่มประสิทธิภาพของฐานข้อมูลจะจัดการได้เป็นอย่างดี
CTE แบบเรียกซ้ำมีความยืดหยุ่นมากกว่า แต่อาจใช้ทรัพยากรมากเมื่อทำงานกับต้นไม้ที่ลึกหรือมีแขนงกว้าง ควรเพิ่มเงื่อนไขจำกัดความลึกในสมาชิกแบบเรียกซ้ำเสมอ เพื่อป้องกันคิวรีที่ทำงานต่อเนื่องไม่สิ้นสุดจากข้อมูลที่ไม่ถูกต้องหรือวงจรที่ไม่คาดคิด
WITH RECURSIVE org_tree AS (
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id, ot.depth + 1
FROM employees e
JOIN org_tree ot ON e.manager_id = ot.id
WHERE ot.depth < 10 -- safety guard: stop at depth 10
)
SELECT name, depth FROM org_tree;กรณีใช้งานจริงที่ต้องใช้การเรียกซ้ำ
แบบจำลองข้อมูลทั่วไปจำนวนมากต้องการการไล่สำรวจที่มีความลึกเท่าใดก็ได้ ซึ่งการเชื่อมตารางกับตัวเองไม่สามารถจัดการได้:
- ต้นไม้หมวดหมู่ — หมวดหมู่สินค้าแบบซ้อนกันในแค็ตตาล็อกอีคอมเมิร์ซ
- รายการชิ้นส่วนประกอบ — ผลิตภัณฑ์ที่ประกอบด้วยชิ้นส่วน ซึ่งแต่ละชิ้นส่วนก็ประกอบด้วยชิ้นส่วนย่อยอีกที
- เธรดความคิดเห็น — การตอบกลับต่อการตอบกลับต่อการตอบกลับ
- เส้นทางระบบไฟล์ — ไดเรกทอรีที่อยู่ภายในไดเรกทอรีอื่น
ในกรณีเหล่านี้ทั้งหมด ควรเลือกใช้ CTE แบบเรียกซ้ำแทนการซ้อนการเชื่อมตารางกับตัวเองหลายครั้ง
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id, name AS full_path
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id,
ct.full_path || ' / ' || c.name
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT id, name, full_path FROM category_tree ORDER BY full_path;ตรวจสอบความรู้ความเข้าใจ
ตรวจสอบความเข้าใจของคุณเกี่ยวกับข้อจำกัดของการเชื่อมตารางกับตัวเองและเวลาที่ควรใช้ CTE แบบเรียกซ้ำแทน
ทบทวนบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ ข้อจำกัดของการเชื่อมตารางกับตัวเอง สำหรับข้อมูลแบบลำดับชั้น:
- การเชื่อมตารางกับตัวเองทำงานได้ดีสำหรับลำดับชั้นที่มี หนึ่งหรือสองระดับที่กำหนดตายตัว
- แต่ละระดับที่เพิ่มขึ้นต้องใช้ JOIN ที่ระบุอย่างชัดเจนอีกหนึ่งรายการ ทำให้คิวรีเปราะบางและดูแลรักษาได้ยาก
- การเชื่อมตารางกับตัวเอง ไม่สามารถจัดการความลึกที่ไม่ทราบแน่ชัด ได้ แถวที่อยู่นอกระดับที่กำหนดตายตัวจะถูกตัดออกโดยไม่แจ้งเตือน
- วิธีนี้ไม่มีการป้องกัน การอ้างอิงแบบวนรอบ ในข้อมูล
- เมื่อความลึกเปลี่ยนแปลงได้หรือไม่ทราบแน่ชัด ให้ใช้ CTE แบบเรียกซ้ำ (
WITH RECURSIVE) แทน - ควรเพิ่ม เงื่อนไขป้องกันความลึก ในคิวรีแบบเรียกซ้ำเสมอ เพื่อป้องกันการทำงานต่อเนื่องไม่สิ้นสุด
การรู้ว่าเมื่อใดควรเปลี่ยนจากการเชื่อมตารางกับตัวเองเป็น CTE แบบเรียกซ้ำ เป็นทักษะสำคัญสำหรับการคิวรีข้อมูลที่มีโครงสร้างเป็นต้นไม้ในภาษาเอสคิวแอล
คำถามที่พบบ่อย
บทเรียน “ข้อจำกัดของการเชื่อมตารางกับตัวเอง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ข้อจำกัดของการเชื่อมตารางกับตัวเอง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส SQL Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส SQL Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ข้อจำกัดของการเชื่อมตารางกับตัวเอง”
กรณีที่ควรใช้การเรียกซ้ำแทน คุณปฏิบัติ SQL Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน SQL Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน SQL Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “ข้อจำกัดของการเชื่อมตารางกับตัวเอง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน SQL Academy นี้ได้ไหม
ได้ บทเรียน SQL Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การเชื่อมตารางกับตัวเองคืออะไร
- พนักงานและผู้จัดการ
- การเปรียบเทียบแถวในตารางเดียวกัน
- ข้อจำกัดของการเชื่อมตารางกับตัวเอง