Undgå uendelige løkker
Dybdebegrænsninger og cyklusdetektion.
Undgå uendelige løkker er en gratis SQL Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i SQL Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. SQL Academy-kurset indeholder 4 lektioner i alt.
Problemet med den uendelige løkke
Rekursive CTE'er er effektive, men de indebærer en alvorlig risiko: Hvis din forespørgsel aldrig når et basistilfælde, vil den køre i en uendelig løkke, bruge al tilgængelig hukommelse og få databasesessionen til at gå ned.
At forstå, hvorfor uendelige løkker opstår, er det første skridt mod at forhindre dem.
Hvornår slutter en løkke aldrig
En rekursiv CTE kører i en uendelig løkke, når det rekursive led bliver ved med at producere nye rækker uden nogensinde at nå en tilstand, hvor der ikke genereres nye rækker.
Det sker typisk i to situationer: En afslutningsbetingelse mangler eller er forkert, eller dataene indeholder en cyklus, hvor node A peger på B, og B peger tilbage på 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;Tilføj en dybdegrænse
Den enkleste sikkerhedsforanstaltning er en dybdetæller. Tilføj en kolonne, der øges med 1 ved hvert rekursive trin, og stop derefter, når den overskrider en maksimal dybde.
Det garanterer, at forespørgslen afsluttes uanset dataene, og den valgte grænse fungerer som et sikkerhedsmaksimum.
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;Dybdegrænse i en hierarkiforespørgsel
Når du gennemgår et medarbejderhierarki, kan du registrere dybden sammen med stien. Klausulen WHERE depth < 5 forhindrer gennemgang af mere end 5 niveauer, selv hvis dataene indeholder dybere eller cirkulære forbindelser.
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;Hvad er cyklusdetektering
En cyklus opstår i grafdata, når du ved at følge kanterne til sidst når tilbage til en node, du allerede har besøgt. For eksempel: A → B → C → A.
En dybdegrænse afslutter stadig forespørgslen ved cykliske data, men den fortæller dig ikke, hvor cyklussen er. Det gør eksplicit cyklusdetektering.
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;Sporing af besøgte noder med et array
En robust teknik til cyklusdetektering er at føre et array med ID'er for besøgte noder gennem rekursionen. Før du besøger den næste node, skal du kontrollere, om den allerede findes i arrayet. Hvis den gør, springer du den over.
PostgreSQL gør dette nemt med operatoren ANY(array) og operatoren || til at føje elementer til et 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-klausulen (PostgreSQL 14+)
PostgreSQL 14 introducerede en indbygget CYCLE-klausul til rekursive CTE'er. Den tilføjer automatisk to kolonner: et boolsk flag, der er true, når der registreres en cyklus, og et array, der registrerer den fulgte sti.
Det er renere end at vedligeholde arrayet manuelt.
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;Kombination af dybdegrænse og cyklusdetektering
Ved at bruge både en dybdegrænse og cyklusdetektering får du den stærkeste sikkerhed:
- Dybdegrænsen fungerer som et fast maksimum uanset datakvaliteten.
- Cyklusdetektering stopper tidligt, så snart en løkke findes, og sparer unødvendige iterationer.
I forespørgsler til produktion bør du altid anvende mindst én af disse sikkerhedsforanstaltninger.
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;Opbygning af hele stien som en streng
Sammen med cyklusdetektering er det nyttigt at registrere hele gennemgangsstien som en læsbar streng. Ved at sammenkæde node-ID'er med -> imellem dem bliver det nemt at vise eller fejlfinde den rute, der blev fulgt gennem grafen.
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;Indstilling af max_recursive_iterations
Nogle databaser (MariaDB, ældre MySQL) bruger en sessionsvariabel til at begrænse rekursionen. I PostgreSQL er den tilsvarende fremgangsmåde at stole på den dybdetæller, du selv skriver, eller bruge tidsgrænser på sætningen.
Indstilling af statement_timeout er et sidste sikkerhedsnet, der afslutter en forespørgsel, som løber løbsk, efter et angivet tidsrum.
-- 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';Valg af den rigtige dybdegrænse
Der findes ingen universel dybdegrænse. Vælg din ud fra den maksimale realistiske dybde i dine data:
- Et organisationsdiagram overstiger sjældent 10-15 niveauer — brug
depth < 20som en passende buffer. - Et filsystemtræ kan være 50-100 niveauer dybt.
- En gennemgang af et socialt netværk begrænses ofte til 3-6 forbindelser.
Sæt grænsen højt nok til at medtage gyldige data, men lavt nok til at opdage forespørgsler, der løber løbsk, tidligt.
-- 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;Dybdegrænser kontra cyklusdetektering
Hvilken teknik bør du bruge?
Opsummering: Sådan holder du rekursive forespørgsler sikre
Her er en opsummering af det, du har lært om at undgå uendelige løkker i rekursive CTE'er:
- Dybdegrænse — tilføj en tællerkolonne, og stop med
WHERE depth < N. Altid effektiv og nem at implementere. - Arraybaseret cyklusdetektering — før ID'er for besøgte noder i et array, og spring alle noder over, der allerede findes i det. Stopper tidligt ved den første cyklus.
- CYCLE-klausul (PostgreSQL 14+) — indbygget syntaks, der automatiserer cyklussporing med kolonnerne
is_cycleogpath. - statement_timeout — et sikkerhedsnet på databaseniveau til forespørgsler, der løber løbsk, men ikke en erstatning for korrekt logik.
- Kombinér både dybdegrænse og cyklusdetektering i produktion for den stærkeste garanti.
Med disse teknikker kan du trygt gennemgå hierarkier og grafer uden at risikere, at databasen går ned.
Lær SQL med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 46
- Lektioner
- 183
Ofte stillede spørgsmål
Er lektionen “Undgå uendelige løkker” gratis?
Ja — hele teksten til “Undgå uendelige løkker” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af SQL Academy-kurset, skal du opgradere til CoddyKit PRO. SQL Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Undgå uendelige løkker”?
Dybdebegrænsninger og cyklusdetektion. Du øver dig i SQL Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på SQL Academy?
Der kræves ingen tidligere erfaring. SQL Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.
Hvor lang tid tager lektionen “Undgå uendelige løkker”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne SQL Academy-lektion?
Ja. Alle SQL Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Sådan fungerer rekursive CTE'er
- Gå gennem et kategoritræ
- Generering af serier og sekvenser
- Undgå uendelige løkker