การหลีกเลี่ยงลูปไม่รู้จบ
จำกัดความลึกและตรวจจับวงจร
การหลีกเลี่ยงลูปไม่รู้จบ เป็นบทเรียน SQL Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL Academy มีบทเรียนทั้งหมด 4 บทเรียน
ปัญหาวงวนไม่สิ้นสุด
CTE แบบเรียกซ้ำมีประสิทธิภาพสูง แต่ก็มีความเสี่ยงร้ายแรง หากคำสั่งสอบถามไม่เคยไปถึงกรณีฐาน คำสั่งจะวนซ้ำตลอดไป ใช้หน่วยความจำที่มีอยู่ทั้งหมด และทำให้เซสชันฐานข้อมูลขัดข้อง
การทำความเข้าใจสาเหตุของวงวนไม่สิ้นสุดคือขั้นตอนแรกในการป้องกันปัญหา
เมื่อใดวงวนจึงไม่สิ้นสุด
CTE แบบเรียกซ้ำจะวนซ้ำไม่สิ้นสุดเมื่อส่วนแบบเรียกซ้ำยังคงสร้างแถวใหม่ โดยไม่เคยไปถึงสถานะที่ไม่มีการสร้างแถวใหม่อีก
เหตุการณ์นี้มักเกิดในสองกรณี: ไม่มีเงื่อนไขสิ้นสุดหรือเงื่อนไขไม่ถูกต้อง หรือข้อมูลมีวงจรที่โหนด A ชี้ไปยัง B และ B ชี้ย้อนกลับมายัง A
-- Simple recursive CTE that WOULD loop forever
-- (do NOT run this as-is; illustration only)
WITH RECURSIVE counter AS (
SELECT 1 AS n -- base case
UNION ALL
SELECT n + 1 -- recursive term
FROM counter
-- no WHERE clause to stop it!
)
SELECT n FROM counter;การเพิ่มขีดจำกัดความลึก
กลไกป้องกันที่ง่ายที่สุดคือตัวนับความลึก เพิ่มคอลัมน์ที่เพิ่มขึ้นทีละ 1 ในทุกขั้นตอนแบบเรียกซ้ำ แล้วหยุดเมื่อค่าเกินความลึกสูงสุด
วิธีนี้รับประกันการสิ้นสุดไม่ว่าข้อมูลจะเป็นอย่างไร และขีดจำกัดที่เลือกจะทำหน้าที่เป็นเพดานความปลอดภัย
WITH RECURSIVE counter AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1
FROM counter
WHERE n < 10 -- stop at depth 10
)
SELECT n FROM counter;ขีดจำกัดความลึกในคำสั่งสอบถามลำดับชั้น
เมื่อเดินสำรวจลำดับชั้นพนักงาน คุณสามารถติดตามความลึกไปพร้อมกับเส้นทางได้ ส่วนคำสั่ง WHERE depth < 5 จะป้องกันไม่ให้เดินสำรวจเกิน 5 ระดับ แม้ข้อมูลจะมีลิงก์ที่ลึกกว่าหรือเป็นวงกลม
CREATE TEMP TABLE employees (
id INT PRIMARY KEY,
name TEXT,
manager_id INT
);
INSERT INTO employees VALUES
(1, 'Alice', NULL),
(2, 'Bob', 1),
(3, 'Carol', 2),
(4, 'Dave', 3);
WITH RECURSIVE hierarchy AS (
SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL -- root
UNION ALL
SELECT e.id, e.name, e.manager_id, h.depth + 1
FROM employees e
JOIN hierarchy h ON e.manager_id = h.id
WHERE h.depth < 5 -- depth limit
)
SELECT id, name, depth FROM hierarchy ORDER BY depth, id;การตรวจจับวงจรคืออะไร
วงจรเกิดขึ้นในข้อมูลกราฟเมื่อการติดตามเส้นเชื่อมไปเรื่อย ๆ แล้วกลับมายังโหนดที่เคยเยี่ยมชมแล้ว ตัวอย่างเช่น A → B → C → A
ขีดจำกัดความลึกยังคงทำให้คำสั่งสอบถามสิ้นสุดลงเมื่อข้อมูลมีวงจร แต่จะไม่บอกคุณว่า ตำแหน่งใดคือวงจร การตรวจจับวงจรโดยตรงทำได้
CREATE TEMP TABLE edges (
from_node INT,
to_node INT
);
-- Introduce a cycle: 1->2->3->1
INSERT INTO edges VALUES
(1, 2),
(2, 3),
(3, 1), -- cycle back to 1
(1, 4); -- also a non-cyclic branch
SELECT * FROM edges;ติดตามโหนดที่เยี่ยมชมแล้วด้วยอาร์เรย์
เทคนิคตรวจจับวงจรที่รัดกุมคือการส่งต่อ อาร์เรย์ของ ID โหนดที่เยี่ยมชมแล้วผ่านการเรียกซ้ำ ก่อนเยี่ยมชมโหนดถัดไป ให้ตรวจสอบว่าโหนดนั้นมีอยู่ในอาร์เรย์แล้วหรือไม่ หากมีอยู่แล้ว ให้ข้ามโหนดนั้น
PostgreSQL ทำให้เรื่องนี้ง่ายด้วยตัวดำเนินการ ANY(array) และตัวดำเนินการต่อท้ายอาร์เรย์ ||
WITH RECURSIVE traverse AS (
-- Start from node 1
SELECT from_node,
to_node,
ARRAY[from_node] AS visited
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node,
e.to_node,
t.visited || e.from_node
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
WHERE NOT (e.from_node = ANY(t.visited)) -- skip visited nodes
)
SELECT from_node, to_node, visited
FROM traverse;ส่วนคำสั่ง CYCLE (PostgreSQL 14+)
PostgreSQL 14 เพิ่มส่วนคำสั่ง CYCLE ในตัวสำหรับ CTE แบบเรียกซ้ำ ส่วนคำสั่งนี้จะเพิ่มสองคอลัมน์โดยอัตโนมัติ ได้แก่ ตัวบ่งชี้ค่าจริง/เท็จที่เป็น true เมื่อตรวจพบวงจร และอาร์เรย์ที่บันทึกเส้นทางที่เดินผ่าน
วิธีนี้เรียบง่ายกว่าการดูแลอาร์เรย์ด้วยตนเอง
WITH RECURSIVE traverse AS (
SELECT from_node, to_node
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node, e.to_node
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
)
CYCLE from_node SET is_cycle USING path
SELECT from_node, to_node, is_cycle, path
FROM traverse;รวมขีดจำกัดความลึกและการตรวจจับวงจร
การใช้ทั้งขีดจำกัดความลึกและการตรวจจับวงจรร่วมกันให้การรับประกันด้านความปลอดภัยที่ดีที่สุด:
- ขีดจำกัดความลึกทำหน้าที่เป็นเพดานตายตัวโดยไม่ขึ้นอยู่กับคุณภาพข้อมูล
- การตรวจจับวงจรจะหยุดทันทีที่พบวงวน ช่วยประหยัดการวนซ้ำที่ไม่จำเป็น
ในคำสั่งสอบถามของระบบจริง ควรใช้กลไกป้องกันอย่างน้อยหนึ่งอย่างเสมอ
WITH RECURSIVE traverse AS (
SELECT from_node,
to_node,
1 AS depth,
ARRAY[from_node] AS visited
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node,
e.to_node,
t.depth + 1,
t.visited || e.from_node
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
WHERE t.depth < 10 -- depth limit
AND NOT (e.from_node = ANY(t.visited)) -- cycle guard
)
SELECT from_node, to_node, depth, visited
FROM traverse;การสร้างเส้นทางเต็มเป็นข้อความ
นอกจากการตรวจจับวงจรแล้ว การบันทึกเส้นทางการเดินสำรวจทั้งหมดเป็นข้อความที่มนุษย์อ่านเข้าใจได้ก็มีประโยชน์ การนำ ID ของโหนดมาต่อกันโดยคั่นด้วย -> ทำให้แสดงหรือแก้ไขข้อบกพร่องของเส้นทางที่เดินผ่านกราฟได้ง่าย
WITH RECURSIVE traverse AS (
SELECT from_node,
to_node,
1 AS depth,
ARRAY[from_node] AS visited,
from_node::TEXT AS path_str
FROM edges
WHERE from_node = 1
UNION ALL
SELECT e.from_node,
e.to_node,
t.depth + 1,
t.visited || e.from_node,
t.path_str || ' -> ' || e.from_node::TEXT
FROM edges e
JOIN traverse t ON e.from_node = t.to_node
WHERE t.depth < 10
AND NOT (e.from_node = ANY(t.visited))
)
SELECT from_node, to_node, path_str, depth
FROM traverse
ORDER BY depth;การกำหนดจำนวนรอบการเรียกซ้ำสูงสุด
ฐานข้อมูลบางชนิด (MariaDB และ MySQL รุ่นเก่า) ใช้ตัวแปรของเซสชันเพื่อจำกัดการเรียกซ้ำ ใน PostgreSQL วิธีการที่เทียบเท่าคือใช้ตัวนับความลึกที่คุณเขียนขึ้นเอง หรือใช้ระยะหมดเวลาระดับคำสั่ง
การตั้งค่า statement_timeout เป็นกลไกป้องกันขั้นสุดท้ายที่จะยุติคำสั่งสอบถามที่ทำงานไม่หยุดหลังจากเวลาที่กำหนด
-- PostgreSQL: set a statement timeout as a safety net
SET statement_timeout = '5s';
-- Now any query that runs longer than 5 seconds is cancelled
WITH RECURSIVE counter AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM counter WHERE n < 1000000
)
SELECT MAX(n) FROM counter;
-- Reset to default when done
SET statement_timeout = '0';การเลือกขีดจำกัดความลึกที่เหมาะสม
ไม่มีขีดจำกัดความลึกที่ใช้ได้กับทุกกรณี ให้เลือกตามความลึกสูงสุดที่เป็นไปได้จริงในข้อมูลของคุณ:
- ผังองค์กรแทบไม่เกิน 10–15 ระดับ — ใช้
depth < 20เป็นเผื่อความปลอดภัยที่เหมาะสม - ต้นไม้ระบบไฟล์อาจลึกถึง 50–100 ระดับ
- การเดินสำรวจกราฟเครือข่ายสังคมมักจำกัดไว้ที่การเชื่อมต่อ 3–6 ขั้น
ตั้งขีดจำกัดให้สูงพอที่จะครอบคลุมข้อมูลที่ถูกต้อง แต่ต่ำพอที่จะตรวจจับคำสั่งสอบถามที่ทำงานไม่หยุดได้ตั้งแต่เนิ่น ๆ
-- Example: org chart with a generous but safe depth cap
WITH RECURSIVE org 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, o.depth + 1
FROM employees e
JOIN org o ON e.manager_id = o.id
WHERE o.depth < 20 -- realistic upper bound for an org chart
)
SELECT id, name, depth
FROM org
ORDER BY depth, name;ขีดจำกัดความลึกเทียบกับการตรวจจับวงจร
คุณควรใช้วิธีใด
ทบทวน: การทำให้คำสั่งสอบถามแบบเรียกซ้ำปลอดภัย
ต่อไปนี้คือสรุปสิ่งที่คุณได้เรียนรู้เกี่ยวกับการหลีกเลี่ยงวงวนไม่สิ้นสุดใน CTE แบบเรียกซ้ำ:
- ขีดจำกัดความลึก — เพิ่มคอลัมน์ตัวนับและหยุดด้วย
WHERE depth < Nมีประสิทธิภาพเสมอและนำไปใช้ได้ง่าย - การตรวจจับวงจรด้วยอาร์เรย์ — เก็บ ID ของโหนดที่เยี่ยมชมแล้วในอาร์เรย์ และข้ามโหนดที่มีอยู่ในอาร์เรย์ หยุดได้ตั้งแต่พบวงจรแรก
- ส่วนคำสั่ง CYCLE (PostgreSQL 14+) — รูปแบบคำสั่งในตัวที่ทำให้การติดตามวงจรเป็นอัตโนมัติด้วยคอลัมน์
is_cycleและpath - ระยะหมดเวลาของคำสั่ง — กลไกป้องกันระดับฐานข้อมูลสำหรับคำสั่งสอบถามที่ทำงานไม่หยุด ไม่ใช่สิ่งทดแทนตรรกะที่ถูกต้อง
- ใช้ทั้งสองวิธีร่วมกัน โดยใช้ขีดจำกัดความลึกและการตรวจจับวงจรในระบบจริงเพื่อการรับประกันที่ดีที่สุด
ด้วยเทคนิคเหล่านี้ คุณจะสามารถเดินสำรวจลำดับชั้นและกราฟได้อย่างมั่นใจโดยไม่เสี่ยงทำให้ฐานข้อมูลขัดข้อง
คำถามที่พบบ่อย
บทเรียน “การหลีกเลี่ยงลูปไม่รู้จบ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การหลีกเลี่ยงลูปไม่รู้จบ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การทำงานของ CTE แบบเรียกซ้ำ
- การเดินผ่านต้นไม้หมวดหมู่
- การสร้างชุดและลำดับ
- การหลีกเลี่ยงลูปไม่รู้จบ