PostgreSQL Antipatterns: β€žΠ‘Π΅Π·ΠΊΡ€Π°ΠΉΠ½ΠΎΡΡ‚Ρ‚Π° Π½Π΅ Π΅ Π³Ρ€Π°Π½ΠΈΡ†Π°Ρ‚Π°!β€œ ΠΈΠ»ΠΈ Малко Π·Π° рСкурсията

рСкурсия - ΠΌΠ½ΠΎΠ³ΠΎ ΠΌΠΎΡ‰Π΅Π½ ΠΈ ΡƒΠ΄ΠΎΠ±Π΅Π½ ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΡŠΠΌ, Π°ΠΊΠΎ ΡΡŠΡ‰ΠΈΡ‚Π΅ β€žΠ·Π°Π΄ΡŠΠ»Π±ΠΎΡ‡Π΅Π½ΠΈβ€œ дСйствия сС ΠΈΠ·Π²ΡŠΡ€ΡˆΠ²Π°Ρ‚ Π²ΡŠΡ€Ρ…Ρƒ ΡΠ²ΡŠΡ€Π·Π°Π½ΠΈ Π΄Π°Π½Π½ΠΈ. Но Π½Π΅ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΠΈΡ€Π°Π½Π°Ρ‚Π° рСкурсия Π΅ Π·Π»ΠΎ, ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΌΠΎΠΆΠ΅ Π΄Π° Π΄ΠΎΠ²Π΅Π΄Π΅ Π΄ΠΎ Π΄Π²Π΅Ρ‚Π΅ Π±Π΅Π·ΠΊΡ€Π°ΠΉΠ½ΠΎ изпълнСниС процСс, ΠΈΠ»ΠΈ (ΠΊΠΎΠ΅Ρ‚ΠΎ сС случва ΠΏΠΎ-чСсто) Π½Π° "изяТданС" Π½Π° цялата Π½Π°Π»ΠΈΡ‡Π½Π° ΠΏΠ°ΠΌΠ΅Ρ‚.

PostgreSQL Antipatterns: β€žΠ‘Π΅Π·ΠΊΡ€Π°ΠΉΠ½ΠΎΡΡ‚Ρ‚Π° Π½Π΅ Π΅ Π³Ρ€Π°Π½ΠΈΡ†Π°Ρ‚Π°!β€œ ΠΈΠ»ΠΈ Малко Π·Π° рСкурсията
Π‘Π£Π‘Π” Π² Ρ‚ΠΎΠ²Π° ΠΎΡ‚Π½ΠΎΡˆΠ΅Π½ΠΈΠ΅ работят Π½Π° ΡΡŠΡ‰ΠΈΡ‚Π΅ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠΈ - "ΠšΠ°Π·Π°Ρ…Π° ΠΌΠΈ Π΄Π° копая, Π·Π°Ρ‚ΠΎΠ²Π° копая". Π’Π°ΡˆΠ°Ρ‚Π° заявка ΠΌΠΎΠΆΠ΅ Π½Π΅ само Π΄Π° Π·Π°Π±Π°Π²ΠΈ ΡΡŠΡΠ΅Π΄Π½ΠΈΡ‚Π΅ процСси, постоянно Π·Π°Π΅ΠΌΠ°ΠΉΠΊΠΈ рСсурси Π½Π° процСсора, Π½ΠΎ ΠΈ Π΄Π° β€žΠΈΠ·ΠΏΡƒΡΠ½Π΅" цялата Π±Π°Π·Π° Π΄Π°Π½Π½ΠΈ, β€žΠΈΠ·ΡΠΆΠ΄Π°ΠΉΠΊΠΈβ€œ цялата Π½Π°Π»ΠΈΡ‡Π½Π° ΠΏΠ°ΠΌΠ΅Ρ‚. Π‘Π»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»Π½ΠΎ Π·Π°Ρ‰ΠΈΡ‚Π° срСщу Π±Π΅Π·ΠΊΡ€Π°ΠΉΠ½Π° рСкурсия - отговорност Π½Π° самия Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ.

Π’ PostgreSQL Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚Ρ‚Π° Π·Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° рСкурсивни заявки Ρ‡Ρ€Π΅Π· WITH RECURSIVE сС появи Π² Π½Π΅Π·Π°ΠΏΠΎΠΌΠ½Π΅Π½ΠΈΡ‚Π΅ Π²Ρ€Π΅ΠΌΠ΅Π½Π° Π½Π° вСрсия 8.4, Π½ΠΎ всС ΠΎΡ‰Π΅ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Ρ€Π΅Π΄ΠΎΠ²Π½ΠΎ Π΄Π° срСщатС ΠΏΠΎΡ‚Π΅Π½Ρ†ΠΈΠ°Π»Π½ΠΎ уязвими β€žΠ±Π΅Π·Π·Π°Ρ‰ΠΈΡ‚Π½ΠΈβ€œ заявки. Как Π΄Π° сС ΠΎΡ‚ΡŠΡ€Π²Π΅Ρ‚Π΅ ΠΎΡ‚ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ ΠΎΡ‚ Ρ‚ΠΎΠ·ΠΈ Ρ€ΠΎΠ΄?

НС ΠΏΠΈΡˆΠ΅Ρ‚Π΅ рСкурсивни заявки

И Π½Π°ΠΏΠΈΡˆΠ΅Ρ‚Π΅ нСрСкурсивни. Π‘ ΡƒΠ²Π°ΠΆΠ΅Π½ΠΈΠ΅, Π’Π°ΡˆΠΈΡΡ‚ K.O.

Π’ΡΡŠΡ‰Π½ΠΎΡΡ‚ PostgreSQL прСдоставя доста функционалност, която ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ Π½Π΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠΈ рСкурсия.

Π˜Π·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠ½ΠΎ Ρ€Π°Π·Π»ΠΈΡ‡Π΅Π½ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ към ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°

Понякога ΠΌΠΎΠΆΠ΅Ρ‚Π΅ просто Π΄Π° ΠΏΠΎΠ³Π»Π΅Π΄Π½Π΅Ρ‚Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° ΠΎΡ‚ β€žΡ€Π°Π·Π»ΠΈΡ‡Π½Π°Ρ‚Π° ΡΡ‚Ρ€Π°Π½Π°β€œ. Π”Π°Π΄ΠΎΡ… ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π·Π° Ρ‚Π°ΠΊΠ°Π²Π° ситуация Π² статията β€žSQL HowTo: 1000 ΠΈ Π΅Π΄ΠΈΠ½ Π½Π°Ρ‡ΠΈΠ½ Π·Π° Π°Π³Ρ€Π΅Π³ΠΈΡ€Π°Π½Π΅β€œ β€” ΡƒΠΌΠ½ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π½Π° Π½Π°Π±ΠΎΡ€ ΠΎΡ‚ числа Π±Π΅Π· ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° пСрсонализирани Π°Π³Ρ€Π΅Π³Π°Ρ‚Π½ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ:

WITH RECURSIVE src AS (
  SELECT '{2,3,5,7,11,13,17,19}'::integer[] arr
)
, T(i, val) AS (
  SELECT
    1::bigint
  , 1
UNION ALL
  SELECT
    i + 1
  , val * arr[i]
  FROM
    T
  , src
  WHERE
    i <= array_length(arr, 1)
)
SELECT
  val
FROM
  T
ORDER BY -- ΠΎΡ‚Π±ΠΎΡ€ Ρ„ΠΈΠ½Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°
  i DESC
LIMIT 1;

Π’Π°Π·ΠΈ заявка ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС Π·Π°ΠΌΠ΅Π½Π΅Π½Π° с опция ΠΎΡ‚ СкспСрти ΠΏΠΎ ΠΌΠ°Ρ‚Π΅ΠΌΠ°Ρ‚ΠΈΠΊΠ°:

WITH src AS (
  SELECT unnest('{2,3,5,7,11,13,17,19}'::integer[]) prime
)
SELECT
  exp(sum(ln(prime)))::integer val
FROM
  src;

Π˜Π·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ generate_series вмСсто Ρ†ΠΈΠΊΠ»ΠΈ

Π”Π° ΠΊΠ°ΠΆΠ΅ΠΌ, Ρ‡Π΅ смС ΠΈΠ·ΠΏΡ€Π°Π²Π΅Π½ΠΈ ΠΏΡ€Π΅Π΄ Π·Π°Π΄Π°Ρ‡Π°Ρ‚Π° Π΄Π° Π³Π΅Π½Π΅Ρ€ΠΈΡ€Π°ΠΌΠ΅ всички възмоТни прСфикси Π·Π° Π½ΠΈΠ· 'abcdefgh':

WITH RECURSIVE T AS (
  SELECT 'abcdefgh' str
UNION ALL
  SELECT
    substr(str, 1, length(str) - 1)
  FROM
    T
  WHERE
    length(str) > 1
)
TABLE T;

Π‘ΠΈΠ³ΡƒΡ€Π½ΠΈ Π»ΠΈ стС, Ρ‡Π΅ ΠΈΠΌΠ°Ρ‚Π΅ Π½ΡƒΠΆΠ΄Π° ΠΎΡ‚ рСкурсия Ρ‚ΡƒΠΊ?.. Ако ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ LATERAL ΠΈ generate_series, Ρ‚ΠΎΠ³Π°Π²Π° Π΄ΠΎΡ€ΠΈ няма Π΄Π° ΠΈΠΌΠ°Ρ‚Π΅ Π½ΡƒΠΆΠ΄Π° ΠΎΡ‚ CTE:

SELECT
  substr(str, 1, ln) str
FROM
  (VALUES('abcdefgh')) T(str)
, LATERAL(
    SELECT generate_series(length(str), 1, -1) ln
  ) X;

ΠŸΡ€ΠΎΠΌΠ΅Π½Π΅Ρ‚Π΅ структурата Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ

НапримСр, ΠΈΠΌΠ°Ρ‚Π΅ Ρ‚Π°Π±Π»ΠΈΡ†Π° със ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ във Ρ„ΠΎΡ€ΡƒΠΌΠ° с Π²Ρ€ΡŠΠ·ΠΊΠΈ ΠΎΡ‚ Ρ‚ΠΎΠ²Π° ΠΊΠΎΠΉ Π½Π° ΠΊΠΎΠ³ΠΎ Π΅ ΠΎΡ‚Π³ΠΎΠ²ΠΎΡ€ΠΈΠ» ΠΈΠ»ΠΈ нишка социална ΠΌΡ€Π΅ΠΆΠ°:

CREATE TABLE message(
  message_id
    uuid
      PRIMARY KEY
, reply_to
    uuid
      REFERENCES message
, body
    text
);
CREATE INDEX ON message(reply_to);

PostgreSQL Antipatterns: β€žΠ‘Π΅Π·ΠΊΡ€Π°ΠΉΠ½ΠΎΡΡ‚Ρ‚Π° Π½Π΅ Π΅ Π³Ρ€Π°Π½ΠΈΡ†Π°Ρ‚Π°!β€œ ΠΈΠ»ΠΈ Малко Π·Π° рСкурсията
Π•, Ρ‚ΠΈΠΏΠΈΡ‡Π½Π° заявка Π·Π° изтСглянС Π½Π° всички ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ ΠΏΠΎ Π΅Π΄Π½Π° Ρ‚Π΅ΠΌΠ° ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° Ρ‚Π°ΠΊΠ°:

WITH RECURSIVE T AS (
  SELECT
    *
  FROM
    message
  WHERE
    message_id = $1
UNION ALL
  SELECT
    m.*
  FROM
    T
  JOIN
    message m
      ON m.reply_to = T.message_id
)
TABLE T;

Но Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ Π²ΠΈΠ½Π°Π³ΠΈ сС Π½ΡƒΠΆΠ΄Π°Π΅ΠΌ ΠΎΡ‚ цялата Ρ‚Π΅ΠΌΠ° ΠΎΡ‚ основното ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅, Ρ‚ΠΎΠ³Π°Π²Π° Π·Π°Ρ‰ΠΎ Π½Π΅ Π½ΠΈ трябва Π΄ΠΎΠ±Π°Π²Π΅Ρ‚Π΅ нСговия ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ към всСки запис Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π΅Π½?

-- Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΏΠΎΠ»Π΅ с ΠΎΠ±Ρ‰ΠΈΠΌ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠΌ Ρ‚Π΅ΠΌΡ‹ ΠΈ индСкс Π½Π° Π½Π΅Π³ΠΎ
ALTER TABLE message
  ADD COLUMN theme_id uuid;
CREATE INDEX ON message(theme_id);

-- ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Ρ‚Π΅ΠΌΡ‹ Π² Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Π΅ ΠΏΡ€ΠΈ вставкС
CREATE OR REPLACE FUNCTION ins() RETURNS TRIGGER AS $$
BEGIN
  NEW.theme_id = CASE
    WHEN NEW.reply_to IS NULL THEN NEW.message_id -- Π±Π΅Ρ€Π΅ΠΌ ΠΈΠ· стартового события
    ELSE ( -- ΠΈΠ»ΠΈ ΠΈΠ· сообщСния, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅ΠΌ
      SELECT
        theme_id
      FROM
        message
      WHERE
        message_id = NEW.reply_to
    )
  END;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER ins BEFORE INSERT
  ON message
    FOR EACH ROW
      EXECUTE PROCEDURE ins();

PostgreSQL Antipatterns: β€žΠ‘Π΅Π·ΠΊΡ€Π°ΠΉΠ½ΠΎΡΡ‚Ρ‚Π° Π½Π΅ Π΅ Π³Ρ€Π°Π½ΠΈΡ†Π°Ρ‚Π°!β€œ ΠΈΠ»ΠΈ Малко Π·Π° рСкурсията
Π‘Π΅Π³Π° цялата Π½ΠΈ рСкурсивна заявка ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС Π½Π°ΠΌΠ°Π»Π΅Π½Π° само Π΄ΠΎ Ρ‚ΠΎΠ²Π°:

SELECT
  *
FROM
  message
WHERE
  theme_id = $1;

Π˜Π·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈ β€žΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚Π΅Π»ΠΈβ€œ

Ако ΠΏΠΎ някаква ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π° Π½Π΅ ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΠΌ структурата Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ, Π½Π΅ΠΊΠ° Π²ΠΈΠ΄ΠΈΠΌ Π½Π° ΠΊΠ°ΠΊΠ²ΠΎ ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° Ρ€Π°Π·Ρ‡ΠΈΡ‚Π°ΠΌΠ΅, Ρ‚Π°ΠΊΠ° Ρ‡Π΅ Π΄ΠΎΡ€ΠΈ Π½Π°Π»ΠΈΡ‡ΠΈΠ΅Ρ‚ΠΎ Π½Π° Π³Ρ€Π΅ΡˆΠΊΠ° Π² Π΄Π°Π½Π½ΠΈΡ‚Π΅ Π΄Π° Π½Π΅ Π²ΠΎΠ΄ΠΈ Π΄ΠΎ Π±Π΅Π·ΠΊΡ€Π°ΠΉΠ½Π° рСкурсия.

Брояч Π½Π° Π΄ΡŠΠ»Π±ΠΎΡ‡ΠΈΠ½Π° Π½Π° рСкурсия

НиС просто ΡƒΠ²Π΅Π»ΠΈΡ‡Π°Π²Π°ΠΌΠ΅ брояча с Π΅Π΄ΠΈΠ½ΠΈΡ†Π° Π½Π° всяка ΡΡ‚ΡŠΠΏΠΊΠ° Π½Π° рСкурсия, Π΄ΠΎΠΊΠ°Ρ‚ΠΎ достигнСм ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΠ΅Ρ‚ΠΎ считамС Π·Π° ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎ Π½Π΅Π°Π΄Π΅ΠΊΠ²Π°Ρ‚Π½ΠΎ:

WITH RECURSIVE T AS (
  SELECT
    0 i
  ...
UNION ALL
  SELECT
    i + 1
  ...
  WHERE
    T.i < 64 -- ΠΏΡ€Π΅Π΄Π΅Π»
)

Pro: ΠšΠΎΠ³Π°Ρ‚ΠΎ сС ΠΎΠΏΠΈΡ‚Π°ΠΌΠ΅ Π΄Π° Π·Π°Ρ†ΠΈΠΊΠ»ΠΈΠΌ, ΠΏΠ°ΠΊ няма Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎΠ²Π΅Ρ‡Π΅ ΠΎΡ‚ опрСдСлСния Π»ΠΈΠΌΠΈΡ‚ ΠΎΡ‚ ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΠΈ β€žΠ² Π΄ΡŠΠ»Π±ΠΎΡ‡ΠΈΠ½Π°β€œ.
ΠΏΡ€ΠΎΡ‚ΠΈΠ²: Няма гаранция, Ρ‡Π΅ няма Π΄Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ ΠΎΡ‚Π½ΠΎΠ²ΠΎ ΡΡŠΡ‰ΠΈΡ запис - Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π½Π° Π΄ΡŠΠ»Π±ΠΎΡ‡ΠΈΠ½Π° 15 ΠΈ 25, Π° слСд Ρ‚ΠΎΠ²Π° Π½Π° всСки +10. И Π½ΠΈΠΊΠΎΠΉ Π½Π΅ ΠΎΠ±Π΅Ρ‰Π° Π½ΠΈΡ‰ΠΎ Π·Π° β€žΡˆΠΈΡ€ΠΎΡ‡ΠΈΠ½Π°Ρ‚Π°β€œ.

Π€ΠΎΡ€ΠΌΠ°Π»Π½ΠΎ ΠΏΠΎΠ΄ΠΎΠ±Π½Π° рСкурсия няма Π΄Π° Π΅ Π±Π΅Π·ΠΊΡ€Π°ΠΉΠ½Π°, Π½ΠΎ Π°ΠΊΠΎ Π½Π° всяка ΡΡ‚ΡŠΠΏΠΊΠ° броят Π½Π° записитС нараства СкспонСнциално, всички Π΄ΠΎΠ±Ρ€Π΅ Π·Π½Π°Π΅ΠΌ ΠΊΠ°ΠΊ Π·Π°Π²ΡŠΡ€ΡˆΠ²Π°...

PostgreSQL Antipatterns: β€žΠ‘Π΅Π·ΠΊΡ€Π°ΠΉΠ½ΠΎΡΡ‚Ρ‚Π° Π½Π΅ Π΅ Π³Ρ€Π°Π½ΠΈΡ†Π°Ρ‚Π°!β€œ ΠΈΠ»ΠΈ Малко Π·Π° рСкурсиятавиТтС β€žΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΡŠΡ‚ със Π·ΡŠΡ€Π½Π°Ρ‚Π° Π½Π° ΡˆΠ°Ρ…ΠΌΠ°Ρ‚Π½Π°Ρ‚Π° Π΄ΡŠΡΠΊΠ°β€œ

ΠŸΠ°Π·ΠΈΡ‚Π΅Π» Π½Π° "ΠΏΡŠΡ‚Π΅ΠΊΠ°Ρ‚Π°"

ДобавямС Π°Π»Ρ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½ΠΎ всички ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΈ Π½Π° ΠΎΠ±Π΅ΠΊΡ‚ΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ срСщнахмС ΠΏΠΎ ΠΏΡŠΡ‚Ρ Π½Π° рСкурсия Π² масив, ΠΊΠΎΠΉΡ‚ΠΎ Π΅ ΡƒΠ½ΠΈΠΊΠ°Π»Π΅Π½ β€žΠΏΡŠΡ‚β€œ към Π½Π΅Π³ΠΎ:

WITH RECURSIVE T AS (
  SELECT
    ARRAY[id] path
  ...
UNION ALL
  SELECT
    path || id
  ...
  WHERE
    id <> ALL(T.path) -- Π½Π΅ совпадаСт Π½ΠΈ с ΠΎΠ΄Π½ΠΈΠΌ ΠΈΠ·
)

Pro: Ако ΠΈΠΌΠ° Ρ†ΠΈΠΊΡŠΠ» Π² Π΄Π°Π½Π½ΠΈΡ‚Π΅, Π½ΠΈΠ΅ Π°Π±ΡΠΎΠ»ΡŽΡ‚Π½ΠΎ няма Π΄Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π²Π°ΠΌΠ΅ ΡΡŠΡ‰ΠΈΡ запис ΠΌΠ½ΠΎΠ³ΠΎΠΊΡ€Π°Ρ‚Π½ΠΎ Π² Ρ€Π°ΠΌΠΊΠΈΡ‚Π΅ Π½Π° Π΅Π΄ΠΈΠ½ ΠΈ ΡΡŠΡ‰ΠΈ ΠΏΡŠΡ‚.
ΠΏΡ€ΠΎΡ‚ΠΈΠ²: Но Π² ΡΡŠΡ‰ΠΎΡ‚ΠΎ Π²Ρ€Π΅ΠΌΠ΅ ΠΌΠΎΠΆΠ΅ΠΌ Π±ΡƒΠΊΠ²Π°Π»Π½ΠΎ Π΄Π° Π·Π°ΠΎΠ±ΠΈΠΊΠΎΠ»ΠΈΠΌ всички записи, Π±Π΅Π· Π΄Π° сС повтарямС.

PostgreSQL Antipatterns: β€žΠ‘Π΅Π·ΠΊΡ€Π°ΠΉΠ½ΠΎΡΡ‚Ρ‚Π° Π½Π΅ Π΅ Π³Ρ€Π°Π½ΠΈΡ†Π°Ρ‚Π°!β€œ ΠΈΠ»ΠΈ Малко Π·Π° рСкурсиятавиТтС "ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌ с Ρ…ΠΎΠ΄Π° Π½Π° коня"

ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅ Π½Π° Π΄ΡŠΠ»ΠΆΠΈΠ½Π°Ρ‚Π° Π½Π° ΠΏΡŠΡ‚Ρ

Π—Π° Π΄Π° ΠΈΠ·Π±Π΅Π³Π½Π΅ΠΌ ситуацията Π½Π° β€žΠ»ΡƒΡ‚Π°Π½Π΅β€œ Π½Π° рСкурсия Π½Π° Π½Π΅Ρ€Π°Π·Π±ΠΈΡ€Π°Π΅ΠΌΠ° Π΄ΡŠΠ»Π±ΠΎΡ‡ΠΈΠ½Π°, ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° ΠΊΠΎΠΌΠ±ΠΈΠ½ΠΈΡ€Π°ΠΌΠ΅ Π΄Π²Π°Ρ‚Π° ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°. Или, Π°ΠΊΠΎ Π½Π΅ искамС Π΄Π° ΠΏΠΎΠ΄Π΄ΡŠΡ€ΠΆΠ°ΠΌΠ΅ Π½Π΅Π½ΡƒΠΆΠ½ΠΈ ΠΏΠΎΠ»Π΅Ρ‚Π°, Π΄ΠΎΠΏΡŠΠ»Π½Π΅Ρ‚Π΅ условиСто Π·Π° ΠΏΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠ°Π²Π°Π½Π΅ Π½Π° рСкурсията с ΠΎΡ†Π΅Π½ΠΊΠ° Π½Π° Π΄ΡŠΠ»ΠΆΠΈΠ½Π°Ρ‚Π° Π½Π° ΠΏΡŠΡ‚Ρ:

WITH RECURSIVE T AS (
  SELECT
    ARRAY[id] path
  ...
UNION ALL
  SELECT
    path || id
  ...
  WHERE
    id <> ALL(T.path) AND
    array_length(T.path, 1) < 10
)

Π˜Π·Π±Π΅Ρ€Π΅Ρ‚Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄ ΠΏΠΎ ваш вкус!

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: www.habr.com

ДобавянС Π½Π° Π½ΠΎΠ² ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€