PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Rekurencyjne dołączanie do Postgresql

Jest to klasyczne użycie prostego rekurencyjnego wyrażenia tabelowego (WITH RECURSIVE ), dostępne w PostgreSQL 8.4 i nowszych.

Zademonstrowano tutaj:http://sqlfiddle.com/#!12/78e15/9

Biorąc pod uwagę przykładowe dane jako SQL:

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

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

Możesz napisać:

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;

To, co robi, to iteracyjne przechodzenie po łańcuchu, dodawanie każdego wiersza do chain tabela jako wskaźniki od i do. Gdy napotka wiersz, dla którego nie istnieje odwołanie „do”, doda dla tego wiersza odwołanie o wartości null „do”. Następna iteracja zauważy, że odwołanie „do” ma wartość NULL i nie generuje wierszy, co powoduje zakończenie iteracji.

Zewnętrzne zapytanie następnie pobiera wiersze, które zostały określone jako koniec łańcucha przez nieistniejący to_id.

Ominięcie rekurencyjnych CTE wymaga trochę wysiłku. Kluczowe kwestie do zrozumienia to:

  • Zaczynają się od wyniku początkowego zapytania, które wielokrotnie łączą z wynikiem „części rekurencyjnej” (zapytanie po UNION lub UNION ALL ), dopóki część cykliczna nie doda żadnych wierszy. To zatrzymuje iterację.

  • Nie są one tak naprawdę rekurencyjne, bardziej iteracyjne, chociaż są dobre w przypadku rzeczy, do których możesz użyć rekurencji.

Więc w zasadzie budujesz tabelę w pętli. Nie możesz usuwać wierszy ani ich zmieniać, tylko dodawać nowe, więc zazwyczaj potrzebujesz zewnętrznego zapytania, które filtruje wyniki, aby uzyskać żądane wiersze wyników. Często dodajesz dodatkowe kolumny zawierające dane pośrednie, których używasz do śledzenia stanu iteracji, kontroli warunków zatrzymania itp.

Pomocne może być przyjrzenie się wynikowi niefiltrowanemu. Jeśli zastąpię końcowe zapytanie podsumowujące prostym łańcuchem SELECT * FROM chain Widzę wygenerowaną tabelę:

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

Pierwszy wiersz to ręcznie dodany wiersz punktu początkowego, w którym określasz, co chcesz wyszukać — w tym przypadku był to vc2 . Każdy kolejny wiersz został dodany przez UNION ed termin rekurencyjny, który wykonuje LEFT OUTER JOIN na poprzednim wyniku i zwraca nowy zestaw wierszy, które łączą poprzednie to_id (teraz w from_id kolumna) do następnego to_id . Jeśli LEFT OUTER JOIN nie pasuje, to to_id będzie null, co spowoduje, że następne wywołanie zwróci teraz wiersze i zakończy iterację.

Ponieważ to zapytanie nie próbuje dodać tylko ostatniego wiersz za każdym razem, w rzeczywistości powtarza sporo pracy w każdej iteracji. Aby tego uniknąć, musiałbyś użyć podejścia bardziej podobnego do Gordona, ale dodatkowo filtruj poprzednie pole głębokości podczas skanowania tabeli wejściowej, dzięki czemu łączysz tylko ostatni wiersz. W praktyce zwykle nie jest to konieczne, ale może być problemem w przypadku bardzo dużych zbiorów danych lub gdy nie można utworzyć odpowiednich indeksów.

Więcej informacji można znaleźć w dokumentacji PostgreSQL na temat CTE.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Zautomatyzowane aktualizacje klastrów PostgreSQL w chmurze niemal zerowe przestoje (część II)

  2. Korzystanie z gniazd replikacji PostgreSQL

  3. Jak uzyskać asynchroniczną / sterowaną zdarzeniami obsługę LISTEN/NOTIFY w Javie przy użyciu bazy danych Postgres?

  4. Warunkowa instrukcja INSERT INTO w postgresie

  5. Tworzenie częściowego unikalnego indeksu za pomocą sqlalchemy na Postgres