sql >> Database teknologi >  >> RDS >> PostgreSQL

Postgresql rekursiv selv joinforbindelse

Dette er en klassisk brug af et simpelt rekursivt almindeligt tabeludtryk (WITH RECURSIVE ), tilgængelig i PostgreSQL 8.4 og nyere.

Demonstreret her:http://sqlfiddle.com/#!12/78e15/9

Givet eksempeldataene som SQL:

CREATE TABLE Table1
    ("ID1" text, "ID2" text)
;

INSERT INTO Table1
    ("ID1", "ID2")
VALUES
    ('vc1', 'vc2'),
    ('vc2', 'vc3'),
    ('vc3', 'vc4'),
    ('vc4', 'rc7')
;
 

Du kan skrive:

WITH RECURSIVE chain(from_id, to_id) AS (
  SELECT NULL, 'vc2'
  UNION
  SELECT c.to_id, t."ID2"
  FROM chain c
  LEFT OUTER JOIN Table1 t ON (t."ID1" = to_id)
  WHERE c.to_id IS NOT NULL
)
SELECT from_id FROM chain WHERE to_id IS NULL;
 

Hvad dette gør, er at gå iterativt gennem kæden og tilføje hver række til chain tabel som fra- og til-pegere. Når den støder på en række, som 'til'-referencen ikke eksisterer for, vil den tilføje en nul-'til'-reference for den række. Den næste iteration vil bemærke, at 'til'-referencen er nul og producerer nul rækker, hvilket får iterationen til at afslutte.

Den ydre forespørgsel opfanger derefter rækker, der er blevet bestemt til at være slutningen af ​​kæden ved at have et ikke-eksisterende to_id.

Det kræver en smule indsats at få hovedet omkring rekursive CTE'er. De vigtigste ting at forstå er:

  • De starter med outputtet af en indledende forespørgsel, som de gentagne gange forbinder med outputtet fra den "rekursive del" (forespørgslen efter UNION eller UNION ALL ), indtil den rekursive del ikke tilføjer rækker. Det stopper iteration.

  • De er ikke rigtig rekursive, mere iterative, selvom de er gode til den slags ting, du måske bruger rekursion til.

Så du bygger dybest set et bord i en løkke. Du kan ikke slette rækker eller ændre dem, kun tilføje nye, så du har generelt brug for en ydre forespørgsel, der filtrerer resultaterne for at få de resultatrækker, du ønsker. Du vil ofte tilføje ekstra kolonner, der indeholder mellemliggende data, som du bruger til at spore iterationens tilstand, kontrollere stopbetingelser osv.

Det kan hjælpe at se på det ufiltrerede resultat. Hvis jeg erstatter den endelige oversigtsforespørgsel med en simpel SELECT * FROM chain Jeg kan se tabellen, der er blevet genereret:

from_id | to_id ---------+------- | vc2 vc2 | vc3 vc3 | vc4 vc4 | rc7 rc7 | (5 rows)

Den første række er den manuelt tilføjede startpunktsrække, hvor du angiver, hvad du vil slå op - i dette tilfælde var det vc2 . Hver efterfølgende række blev tilføjet af UNION ed rekursiv term, som gør en LEFT OUTER JOIN på det forrige resultat og returnerer et nyt sæt rækker, der parrer den forrige to_id (nu i from_id kolonne) til den næste to_id . Hvis LEFT OUTER JOIN stemmer ikke overens med to_id vil være null, hvilket får den næste kald til at returnere nu rækker og afslutte iteration.

Fordi denne forespørgsel ikke forsøger kun at tilføje den sidste række hver gang, det gentager faktisk en del arbejde hver iteration. For at undgå det skal du bruge en tilgang mere som Gordons, men derudover filtrere på det forrige dybdefelt, når du scannede inputtabel, så du kun sluttede dig til den seneste række. I praksis er dette normalt ikke nødvendigt, men det kan være et problem for meget store datasæt, eller hvor du ikke kan oprette passende indekser.

Mere kan læres i PostgreSQL-dokumentationen om CTE'er.



  1. Forskel i MySQL JOIN vs LEFT JOIN

  2. Hvordan skriver man parametriseret orakelindsæt-forespørgsel?

  3. Indsamlingsmetode:FINDER Funktion i Oracle-databasen

  4. Undersøg kontrolkilden for alle kontroller i dit MS Access-projekt