การเดินผ่านต้นไม้หมวดหมู่
ขยายต้นไม้พ่อแม่-ลูกให้ครบถ้วน
การเดินผ่านต้นไม้หมวดหมู่ เป็นบทเรียน SQL Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL Academy มีบทเรียนทั้งหมด 4 บทเรียน
ต้นไม้หมวดหมู่คืออะไร
ชุดข้อมูลในโลกจริงจำนวนมากมีความสัมพันธ์ระหว่างโหนดแม่กับโหนดลูก แค็ตตาล็อกสินค้าอาจมีหมวดหมู่ เช่น อุปกรณ์อิเล็กทรอนิกส์ → โทรศัพท์ → สมาร์ตโฟน แต่ละโหนดมีโหนดแม่ จึงก่อให้เกิดโครงสร้างต้นไม้
ในภาษาเอสคิวแอล โดยทั่วไปจะจัดเก็บข้อมูลนี้เป็นตารางที่อ้างอิงตัวเอง โดยแต่ละแถวมี id และ parent_id ซึ่งชี้ไปยังอีกแถวหนึ่งในตารางเดียวกัน
CREATE TABLE categories (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
parent_id INT REFERENCES categories(id)
);ข้อมูลหมวดหมู่ตัวอย่าง
มาสร้างต้นไม้หมวดหมู่ขนาดเล็กกัน โหนดรากมี parent_id = NULL เพราะไม่มีโหนดแม่ ส่วนโหนดอื่นทุกโหนดจะชี้ไปยังโหนดแม่ด้วยค่า parent_id ที่ไม่เป็นค่าว่าง
INSERT INTO categories (id, name, parent_id) VALUES
(1, 'Electronics', NULL),
(2, 'Phones', 1),
(3, 'Laptops', 1),
(4, 'Smartphones', 2),
(5, 'Feature Phones', 2),
(6, 'Gaming Laptops', 3),
(7, 'Ultrabooks', 3);ปัญหาของคิวรีแบบง่าย
SELECT ธรรมดาสามารถดึงข้อมูลได้ทีละระดับเท่านั้น หากต้องการเข้าถึงข้อมูลที่ลึกสามระดับ คุณจะต้องใช้คิวรีแยกกันสามชุดหรือการเชื่อมตารางกับตัวเองสามครั้ง ซึ่งจะจัดการได้ยากขึ้นเมื่อต้นไม้ขยายใหญ่ขึ้น
WITH RECURSIVE แก้ปัญหานี้ด้วยการเปิดให้คิวรีอ้างอิงผลลัพธ์ของตัวเอง และไล่ไปทีละระดับจนไม่พบแถวใหม่
-- This only shows direct children of Electronics (level 1)
SELECT id, name
FROM categories
WHERE parent_id = 1;โครงสร้างของ WITH RECURSIVE
CTE แบบเรียกซ้ำมีสองส่วนที่คั่นด้วย UNION ALL:
1. สมาชิกฐาน — SELECT ปกติที่ให้แถวเริ่มต้น
2. สมาชิกแบบเรียกซ้ำ — SELECT ที่เชื่อม CTE กลับเข้ากับตัวมันเอง โดยสร้างระดับถัดไปในการวนซ้ำแต่ละครั้ง
ระบบประมวลผลจะทำซ้ำสมาชิกแบบเรียกซ้ำจนกว่าจะไม่ส่งคืนแถวใดเลย
WITH RECURSIVE cte AS (
-- Anchor: starting rows
SELECT ...
UNION ALL
-- Recursive: join cte to base table
SELECT ... FROM base_table JOIN cte ON ...
)
SELECT * FROM cte;เดินสำรวจต้นไม้ทั้งหมดจากราก
เริ่มจากราก (ตำแหน่งที่ parent_id IS NULL) แล้วเดินสำรวจลงไปยังลูกหลานทุกโหนด สมาชิกแบบเรียกซ้ำจะเชื่อมแต่ละแถวที่สะสมไว้กลับเข้ากับ categories ตามความสัมพันธ์ระหว่างแม่กับลูก
WITH RECURSIVE category_tree AS (
-- Anchor: root nodes
SELECT id, name, parent_id, 1 AS depth
FROM categories
WHERE parent_id IS NULL
UNION ALL
-- Recursive: children of current level
SELECT c.id, c.name, c.parent_id, ct.depth + 1
FROM categories c
JOIN category_tree ct ON ct.id = c.parent_id
)
SELECT id, name, depth
FROM category_tree
ORDER BY depth, id;การติดตามเส้นทาง
การบันทึกเส้นทางเต็มจากรากไปยังแต่ละโหนดมีประโยชน์ คุณสามารถสร้างข้อความ path ด้วยการนำชื่อบรรพบุรุษมาต่อกันขณะเรียกซ้ำลึกลงไป
วิธีนี้ช่วยให้แสดงเส้นทางนำทาง เช่น อุปกรณ์อิเล็กทรอนิกส์ / โทรศัพท์ / สมาร์ตโฟน ได้อย่างง่ายดาย
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id,
name AS path
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id,
ct.path || ' / ' || c.name
FROM categories c
JOIN category_tree ct ON ct.id = c.parent_id
)
SELECT id, name, path
FROM category_tree
ORDER BY path;เริ่มจากโหนดที่ระบุ
คุณไม่จำเป็นต้องเริ่มจากราก โดยเปลี่ยนส่วนคำสั่ง WHERE ของสมาชิกฐาน คุณก็สามารถเดินสำรวจต้นไม้ย่อยของโหนดใดก็ได้ ในที่นี้เราเริ่มจาก โทรศัพท์ (รหัส = 2) และดึงลูกหลานทั้งหมดของโหนดนี้
WITH RECURSIVE subtree AS (
SELECT id, name, parent_id, 0 AS depth
FROM categories
WHERE id = 2 -- start at Phones
UNION ALL
SELECT c.id, c.name, c.parent_id, s.depth + 1
FROM categories c
JOIN subtree s ON s.id = c.parent_id
)
SELECT id, name, depth
FROM subtree
ORDER BY depth, id;เดินสำรวจขึ้นด้านบน: ค้นหาบรรพบุรุษทั้งหมด
คุณสามารถเดินสำรวจต้นไม้ในทิศทางย้อนกลับได้เช่นกัน โดยเดินขึ้นจากโหนดปลายไปยังราก เพียงสลับการเชื่อมเพื่อให้ติดตาม parent_id ขึ้นด้านบนแทนการลงด้านล่าง วิธีนี้มีประโยชน์เมื่อต้องการเส้นทางนำทางทั้งหมดของโหนดปลายที่ทราบอยู่แล้ว
WITH RECURSIVE ancestors AS (
SELECT id, name, parent_id
FROM categories
WHERE id = 4 -- start at Smartphones
UNION ALL
SELECT c.id, c.name, c.parent_id
FROM categories c
JOIN ancestors a ON a.parent_id = c.id
)
SELECT id, name
FROM ancestors
ORDER BY id;เพิ่มการแสดงผลแบบเยื้อง
รูปแบบทั่วไปของส่วนติดต่อผู้ใช้คือการเยื้องโหนดลูกให้เห็นทางภาพ คุณสามารถใช้ REPEAT (หรือ LPAD) ร่วมกับคอลัมน์ depth เพื่อเติมช่องว่างหน้าชื่อแต่ละชื่อ ทำให้เกิดมุมมองต้นไม้แบบข้อความ
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id, 0 AS depth
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id, ct.depth + 1
FROM categories c
JOIN category_tree ct ON ct.id = c.parent_id
)
SELECT
REPEAT(' ', depth) || name AS indented_name,
depth
FROM category_tree
ORDER BY path;ป้องกันวงวนไม่สิ้นสุด
หากข้อมูลมีวงจร (A เป็นแม่ของ B และ B เป็นแม่ของ A) การเรียกซ้ำจะทำงานไม่สิ้นสุดและทำให้ระบบขัดข้อง คุณสามารถป้องกันปัญหานี้ได้ด้วยการติดตาม ID ที่เยี่ยมชมแล้วในอาร์เรย์ และหยุดเมื่อ ID ปัจจุบันมีอยู่ในอาร์เรย์แล้ว
WITH RECURSIVE safe_tree AS (
SELECT id, name, parent_id,
ARRAY[id] AS visited
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id,
st.visited || c.id
FROM categories c
JOIN safe_tree st ON st.id = c.parent_id
WHERE c.id <> ALL(st.visited) -- stop if already seen
)
SELECT id, name FROM safe_tree;นับลูกหลานของแต่ละโหนด
เมื่อมีต้นไม้ทั้งหมดแล้ว คุณสามารถรวมผลได้ ในที่นี้เรานับจำนวนลูกหลานของแต่ละโหนดโดยจัดกลุ่มแถวลูกโดยอ้างอิงกับรายการบรรพบุรุษ วิธีนี้มีประโยชน์สำหรับแสดงจำนวนรายการถัดจากชื่อหมวดหมู่ในเมนูนำทาง
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id, id AS root_id
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id, ct.root_id
FROM categories c
JOIN category_tree ct ON ct.id = c.parent_id
)
SELECT
root_id,
COUNT(*) - 1 AS descendant_count
FROM category_tree
GROUP BY root_id
ORDER BY root_id;ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจของคุณเกี่ยวกับคำสั่งสอบถามต้นไม้หมวดหมู่แบบเรียกซ้ำ
ทบทวนบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้วิธีเดินสำรวจตารางหมวดหมู่ที่อ้างอิงถึงตัวเองโดยใช้ WITH RECURSIVE
ประเด็นสำคัญ:
- สมาชิกฐาน เลือกโหนดเริ่มต้น ซึ่งโดยปกติคือราก
- สมาชิกแบบเรียกซ้ำ เชื่อม CTE กลับเข้ากับตารางฐานเพื่อค้นหาระดับถัดไป
- เพิ่มคอลัมน์ depth เพื่อติดตามว่าแต่ละโหนดอยู่ลึกกี่ระดับ
- สร้างข้อความ path เพื่อสร้างเส้นทางนำทาง
- เดินสำรวจ ขึ้นด้านบน โดยติดตาม parent_id ย้อนกลับเพื่อค้นหาบรรพบุรุษทั้งหมด
- ใช้อาร์เรย์ visited เพื่อป้องกันวงจรในข้อมูลที่มีปัญหา
คำถามที่พบบ่อย
บทเรียน “การเดินผ่านต้นไม้หมวดหมู่” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การเดินผ่านต้นไม้หมวดหมู่” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส SQL Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส SQL Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การเดินผ่านต้นไม้หมวดหมู่”
ขยายต้นไม้พ่อแม่-ลูกให้ครบถ้วน คุณปฏิบัติ SQL Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน SQL Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน SQL Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “การเดินผ่านต้นไม้หมวดหมู่” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน SQL Academy นี้ได้ไหม
ได้ บทเรียน SQL Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การทำงานของ CTE แบบเรียกซ้ำ
- การเดินผ่านต้นไม้หมวดหมู่
- การสร้างชุดและลำดับ
- การหลีกเลี่ยงลูปไม่รู้จบ