PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Postgresql рекурсивно самостоятелно присъединяване

Това е класическо използване на прост рекурсивен израз на обща таблица (WITH RECURSIVE ), наличен в PostgreSQL 8.4 и по-нови версии.

Демонстрирано тук:http://sqlfiddle.com/#!12/78e15/9

Като се имат предвид примерните данни като SQL:

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

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

Можете да напишете:

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;

Това, което прави, е итеративно да върви по веригата, като добавя всеки ред към chain таблица като указатели от и до. Когато срещне ред, за който препратката „до“ не съществува, тя ще добави нулева препратка „до“ за този ред. Следващата итерация ще забележи, че препратката „до“ е нула и ще произведе нула редове, което води до края на итерацията.

След това външната заявка взема редове, за които е определено, че са края на веригата, като има несъществуващ to_id.

Необходими са малко усилия, за да се запознаете с рекурсивните CTE. Те ключовите неща, които трябва да разберете, са:

  • Те започват с изхода на първоначална заявка, която многократно обединяват с изхода на "рекурсивната част" (заявката след UNION или UNION ALL ), докато рекурсивната част не добавя редове. Това спира повторението.

  • Те всъщност не са рекурсивни, по-итеративни, въпреки че са добри за неща, за които може да използвате рекурсия.

Така че по същество изграждате таблица в цикъл. Не можете да изтривате редове или да ги променяте, а само да добавяте нови, така че обикновено имате нужда от външна заявка, която филтрира резултатите, за да получите желаните от вас редове с резултати. Често ще добавяте допълнителни колони, съдържащи междинни данни, които използвате за проследяване на състоянието на итерацията, контрол на условията за спиране и т.н.

Може да помогне да погледнете нефилтрирания резултат. Ако заменя окончателната обобщена заявка с проста SELECT * FROM chain Виждам таблицата, която е генерирана:

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

Първият ред е ръчно добавеният ред за начална точка, където посочвате какво искате да търсите - в този случай това е vc2 . Всеки следващ ред беше добавен от UNION ed рекурсивен термин, който извършва LEFT OUTER JOIN върху предишния резултат и връща нов набор от редове, които свързват предишния to_id (сега в from_id колона) към следващия to_id . Ако LEFT OUTER JOIN не съвпада тогава to_id ще бъде null, което кара следващото извикване да върне редовете сега и да приключи итерацията.

Тъй като тази заявка не се опитва да добави само последната ред всеки път, той всъщност повтаря доста работа на всяка итерация. За да избегнете това, ще трябва да използвате подход, по-скоро подобен на Гордън, но допълнително да филтрирате по предишното поле за дълбочина, когато сте сканирали таблица за въвеждане, така че сте се присъединили само към последния ред. На практика това обикновено не е необходимо, но може да е проблем за много големи набори от данни или когато не можете да създадете подходящи индекси.

Повече можете да научите в документацията на PostgreSQL за CTEs.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Кой е най-препоръчителният начин за съхраняване на време в PostgreSQL с помощта на Java?

  2. Сортиране на дърво с материализиран път?

  3. Еквивалент на PostgreSQL DATEADD().

  4. Функции на прозореца или общи таблични изрази:пребройте предишните редове в рамките на диапазона

  5. Как работи функцията Degrees() в PostgreSQL