PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข

ืื™ืŸ ืงืึธืžืคึผืœืขืงืก ืขืจืคึผ ืกื™ืกื˜ืขืžืขืŸ ืคื™ืœืข ืขื ื˜ื™ื˜ื™ื– ื”ืึธื‘ืŸ ืึท ื›ื™ื™ืขืจืึทืจืงืึทืงืึทืœ ื ืึทื˜ื•ืจื•ื•ืขืŸ ื›ืึธื•ืžืึทื“ื–ืฉื™ื ื™ืึทืก ืึทื‘ื“ื–ืฉืขืงืฅ ืฉื•ืจื” ืึทืจื•ื™ืฃ ืื™ืŸ ื‘ื•ื™ื ืคื•ืŸ ืึธื•ื•ืขืก-ืึธืคึผืฉื˜ืึทืžืœื™ื ื’ ื‘ืึทืฆื™ื•ื ื’ืขืŸ - ื“ืึธืก ืื™ื– ื“ื™ ืึธืจื’ืึทื ืึทื–ื™ื™ืฉืึทื ืึทืœ ืกื˜ืจื•ืงื˜ื•ืจ ืคื•ืŸ ื“ื™ ืคืึทืจื ืขืžื•ื ื’ (ืึทืœืข ื“ื™ ืฆื•ื•ื™ื™ื’ืŸ, ื“ื™ืคึผืึทืจื˜ืžืึทื ืฅ ืื•ืŸ ืึทืจื‘ืขื˜ ื’ืจื•ืคึผืขืก), ืื•ืŸ ื“ืขืจ ืงืึทื˜ืึทืœืึธื’ ืคื•ืŸ ืกื›ื•ื™ืจืข ืื•ืŸ ืึทืจื‘ืขื˜ ื’ืขื‘ื™ื˜ืŸ, ืื•ืŸ ื“ื™ ื’ืขืึธื’ืจืึทืคื™ ืคื•ืŸ ืคืืจืงื•ื™ืคื•ื ื’ ืคื•ื ืงื˜ืŸ, ...

PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข

ืื™ืŸ ืคืึทืงื˜, ืขืก ืื™ื– ื’ืึธืจื ื™ื˜ ื’ืขืฉืขืคื˜ ืึธื˜ืึทืžื™ื™ืฉืึทืŸ ื’ืขื‘ื™ื˜ืŸ, ื•ื•ื• ืขืก ื•ื•ืึธืœื˜ ื ื™ืฉื˜ ื–ื™ื™ืŸ ืงื™ื™ืŸ ื›ื™ื™ืขืจืึทืจืงื™ ื•ื•ื™ ืึท ืจืขื–ื•ืœื˜ืึทื˜. ืึธื‘ืขืจ ืืคื™ืœื• ืื•ื™ื‘ ืื™ืจ ื˜ืึธืŸ ื ื™ื˜ ืึทืจื‘ืขื˜ "ืคึฟืึทืจ ื“ื™ ื’ืขืฉืขืคื˜," ืื™ืจ ืงืขื ืขืŸ ื ืึธืš ืœื™ื™ื›ื˜ ื˜ืจืขืคืŸ ื›ื™ื™ืขืจืึทืจืงืึทืงืึทืœ ื‘ืืฆื™ื•ื ื’ืขืŸ. ืขืก ืื™ื– ืคึผืฉื•ื˜, ืืคื™ืœื• ื“ื™ื™ืŸ ืžืฉืคึผื—ื” ื‘ื•ื™ื ืึธื“ืขืจ ืฉื˜ืึธืง ืคึผืœืึทืŸ ืคื•ืŸ ืœืึธืงืึทืœ ืื™ืŸ ืึท ืฉืึทืคึผื™ื ื’ ืฆืขื ื˜ืขืจ ืื™ื– ื“ื™ ื–ืขืœื‘ืข ืกื˜ืจื•ืงื˜ื•ืจ.

ืขืก ื–ืขื ืขืŸ ืคื™ืœืข ื•ื•ืขื’ืŸ ืฆื• ืงืจืึธื ืึทื–ืึท ืึท ื‘ื•ื™ื ืื™ืŸ ืึท DBMS, ืึธื‘ืขืจ ื”ื™ื™ึทื ื˜ ืžื™ืจ ื•ื•ืขืœืŸ ืคืึธืงื•ืก ืื•ื™ืฃ ื‘ืœื•ื™ื– ืื™ื™ืŸ ืึธืคึผืฆื™ืข:

CREATE TABLE hier(
  id
    integer
      PRIMARY KEY
, pid
    integer
      REFERENCES hier
, data
    json
);

CREATE INDEX ON hier(pid); -- ะฝะต ะทะฐะฑั‹ะฒะฐะตะผ, ั‡ั‚ะพ FK ะฝะต ะฟะพะดั€ะฐะทัƒะผะตะฒะฐะตั‚ ะฐะฒั‚ะพัะพะทะดะฐะฝะธะต ะธะฝะดะตะบัะฐ, ะฒ ะพั‚ะปะธั‡ะธะต ะพั‚ PK

ืื•ืŸ ื‘ืฉืขืช ืื™ืจ ืงื•ืง ืื™ืŸ ื“ื™ ื˜ื™ืคืขื ื™ืฉืŸ ืคื•ืŸ ื“ืขืจ ื›ื™ื™ืขืจืึทืจืงื™, ืขืก ืื™ื– ื’ืขื“ื•ืœื“ื™ืง ื•ื•ืืจื˜ืŸ ืฆื• ื–ืขืŸ ื•ื•ื™ [ืื™ืŸ] ืขืคืขืงื˜ื™ื•ื• ื“ื™ื™ืŸ "ื ืึทื™ื•ื•" ื•ื•ืขื’ืŸ ืคื•ืŸ ืืจื‘ืขื˜ืŸ ืžื™ื˜ ืึทื–ืึท ืึท ืกื˜ืจื•ืงื˜ื•ืจ ื•ื•ืขื˜ ื–ื™ื™ืŸ.

PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข
ื–ืืœ ืก ืงื•ืง ืื™ืŸ ื˜ื™ืคึผื™ืฉ ืคึผืจืึธื‘ืœืขืžืก ื•ื•ืึธืก ืฉื˜ื™ื™ืขืŸ, ื–ื™ื™ืขืจ ื™ืžืคึผืœืึทืžืขื ื˜ื™ื™ืฉืึทืŸ ืื™ืŸ SQL, ืื•ืŸ ืคึผืจื•ื‘ื™ืจืŸ ืฆื• ืคึฟืึทืจื‘ืขืกืขืจืŸ ื–ื™ื™ืขืจ ืคืึธืจืฉื˜ืขืœื•ื ื’.

#1. ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš?

ืœืึธืžื™ืจ, ืคึฟืึทืจ ื–ื™ื›ืขืจืงื™ื™ื˜, ืึธื ื ืขืžืขืŸ, ืึทื– ื“ื™ ื“ืึธื–ื™ืงืข ืกื˜ืจื•ืงื˜ื•ืจ ื•ื•ืขื˜ ืึธืคึผืฉืคึผื™ื’ืœืขืŸ ื“ื™ ืื•ื ื˜ืขืจื ืขืžื•ื ื’ ืคึฟื•ืŸ ื“ื™ ืึธืคึผื˜ื™ื™ืœืŸ ืื™ืŸ ื“ืขืจ ืกื˜ืจื•ืงื˜ื•ืจ ืคึฟื•ืŸ ื“ืขืจ ืึธืจื’ืึทื ื™ื–ืึทืฆื™ืข: ืึธืคึผื˜ื™ื™ืœืŸ, ืึธืคึผื˜ื™ื™ืœืŸ, ืกืขืงื˜ืึธืจืŸ, ืฆื•ื•ื™ื™ื’ืŸ, ืึทืจื‘ืขื˜ึพื’ืจื•ืคึผืขืก... โ€” ื•ื•ืขืœื›ืขืจ ืื™ืจ ืจื•ืคึฟื˜ ื–ื™ื™.
PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข

ืขืจืฉื˜ืขืจ, ืœืึธื–ืŸ ืื•ื ื“ื– ื“ื–ืฉืขื ืขืจื™ื™ื˜ ืื•ื ื“ื–ืขืจ 'ื‘ื•ื™ื' ืคื•ืŸ 10 ืง ืขืœืขืžืขื ื˜ืŸ

INSERT INTO hier
WITH RECURSIVE T AS (
  SELECT
    1::integer id
  , '{1}'::integer[] pids
UNION ALL
  SELECT
    id + 1
  , pids[1:(random() * array_length(pids, 1))::integer] || (id + 1)
  FROM
    T
  WHERE
    id < 10000
)
SELECT
  pids[array_length(pids, 1)] id
, pids[array_length(pids, 1) - 1] pid
FROM
  T;

ืœืึธืžื™ืจ ืึธื ื”ื™ื™ื‘ืŸ ืžื™ื˜ ื“ื™ ืกื™ืžืคึผืœืึทืกื˜ ืึทืจื‘ืขื˜ - ื’ืขืคึฟื™ื ืขืŸ ืึทืœืข ืขืžืคึผืœื•ื™ื™ื– ื•ื•ืึธืก ืึทืจื‘ืขื˜ ืื™ืŸ ืึท ืกืคึผืขืฆื™ืคื™ืฉ ืกืขืงื˜ืึธืจ, ืึธื“ืขืจ ืื™ืŸ ื˜ืขืจืžื™ื ืขืŸ ืคื•ืŸ ื›ื™ื™ืขืจืึทืจืงื™ - ื’ืขืคึฟื™ื ืขืŸ ืึทืœืข ืงื™ื ื“ืขืจ ืคื•ืŸ ืึท ื ืึธื“ืข. ืขืก ื•ื•ืึธืœื˜ ืื•ื™ืš ื’ืขื•ื•ืขืŸ ืคื™ื™ึทืŸ ืฆื• ื‘ืึทืงื•ืžืขืŸ ื“ื™ "ื˜ื™ืคืงื™ื™ึทื˜" ืคื•ืŸ ื“ื™ ืึธืคึผืฉื˜ืึทืžืœื™ื ื’ ... ื“ืึธืก ืึทืœืฅ ืงืขืŸ ื–ื™ื™ืŸ ื ื™ื™ื˜ื™ืง, ืœืžืฉืœ, ืฆื• ื‘ื•ื™ืขืŸ ืึท ืžื™ืŸ ืคื•ืŸ ืงืึธืžืคึผืœืขืงืก ืกืขืœืขืงืฆื™ืข ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ื“ืขืจ ืจืฉื™ืžื” ืคื•ืŸ ื™ื“ืก ืคื•ืŸ ื“ื™ ืขืžืคึผืœื•ื™ื™ื–.

ืึทืœืฅ ื•ื•ืึธืœื˜ ื–ื™ื™ืŸ ื’ื•ื˜ ืื•ื™ื‘ ืขืก ื–ืขื ืขืŸ ื‘ืœื•ื™ื– ืึท ืคึผืึธืจ ืคื•ืŸ ืœืขื•ื•ืขืœืก ืคื•ืŸ ื“ื™ ืงื™ื ื“ืกืงื™ื ื“ืขืจ ืื•ืŸ ื“ื™ ื ื•ืžืขืจ ืื™ื– ื™ืŸ ืึท ื˜ื•ืฅ, ืึธื‘ืขืจ ืื•ื™ื‘ ืขืก ื–ืขื ืขืŸ ืžืขืจ ื•ื•ื™ 5 ืœืขื•ื•ืขืœืก, ืื•ืŸ ืขืก ื–ืขื ืขืŸ ืฉื•ื™ืŸ ื“ืึทื–ืึทื ื– ืคื•ืŸ ืงื™ื ื“ืกืงื™ื ื“ืขืจ, ืขืก ืงืขืŸ ื–ื™ื™ืŸ ืคึผืจืึธื‘ืœืขืžืก. ื–ืืœ ืก ืงื•ืง ืื™ืŸ ื•ื•ื™ ื˜ืจืื“ื™ืฆื™ืื ืขืœืŸ ืึทืจืึธืคึผ-ื‘ื•ื™ื ื–ื•ื›ืŸ ืึธืคึผืฆื™ืขืก ื–ืขื ืขืŸ ื’ืขืฉืจื™ื‘ืŸ (ืื•ืŸ ืึทืจื‘ืขื˜). ืึธื‘ืขืจ ืขืจืฉื˜ืขืจ, ืœืึธื–ืŸ ืก ื‘ืึทืฉืœื™ืกืŸ ื•ื•ืึธืก ื ืึธื•ื“ื– ื•ื•ืขื˜ ื–ื™ื™ืŸ ื“ื™ ืžืขืจืกื˜ ื˜ืฉื™ืงืึทื•ื•ืข ืคึฟืึทืจ ืื•ื ื“ื–ืขืจ ืคืึธืจืฉื•ื ื’.

Most "ื˜ื™ืฃ" ืกื•ื‘ื˜ืจื™ื™ื:

WITH RECURSIVE T AS (
  SELECT
    id
  , pid
  , ARRAY[id] path
  FROM
    hier
  WHERE
    pid IS NULL
UNION ALL
  SELECT
    hier.id
  , hier.pid
  , T.path || hier.id
  FROM
    T
  JOIN
    hier
      ON hier.pid = T.id
)
TABLE T ORDER BY array_length(path, 1) DESC;

 id  | pid  | path
---------------------------------------------
7624 | 7623 | {7615,7620,7621,7622,7623,7624}
4995 | 4994 | {4983,4985,4988,4993,4994,4995}
4991 | 4990 | {4983,4985,4988,4989,4990,4991}
...

Most "ื‘ืจื™ื™ื˜" ืกื•ื‘ื˜ืจื™ื™ื:

...
SELECT
  path[1] id
, count(*)
FROM
  T
GROUP BY
  1
ORDER BY
  2 DESC;

id   | count
------------
5300 |   30
 450 |   28
1239 |   27
1573 |   25

ืคึฟืึทืจ ื“ื™ ืคึฟืจืื’ืŸ ืžื™ืจ ื’ืขื•ื•ื™ื™ื ื˜ ื“ื™ ื˜ื™ืคึผื™ืฉ ืจืขืงื•ืจืกื™ื•ื•ืข JOIN:
PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข

ื“ืึธืš, ืžื™ื˜ ื“ืขื ื‘ืขื˜ืŸ ืžืึธื“ืขืœ ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ื™ื˜ืขืจื™ื™ืฉืึทื ื– ื•ื•ืขื˜ ื’ืœื™ื™ึทื›ืŸ ื“ื™ ื’ืึทื ืฅ ื ื•ืžืขืจ ืคื•ืŸ ืงื™ื ื“ืกืงื™ื ื“ืขืจ (ืื•ืŸ ืขืก ื–ืขื ืขืŸ ืขื˜ืœืขื›ืข ื˜ื•ืฅ ืคื•ืŸ ื–ื™ื™), ืื•ืŸ ื“ืึธืก ืงืขืŸ ื ืขืžืขืŸ ื’ืึทื ืฅ ื‘ืึทื˜ื™ื™ึทื˜ื™ืง ืจืขืกื•ืจืกืŸ, ืื•ืŸ, ื•ื•ื™ ืึท ืจืขื–ื•ืœื˜ืึทื˜, ืฆื™ื™ื˜.

ืœืึธืžื™ืจ ืงืึธื ื˜ืจืึธืœื™ืจืŸ ื“ื™ "ื‘ืจื™ื™ื˜ืึทืกื˜" ืกื•ื‘ื˜ืจื™:

WITH RECURSIVE T AS (
  SELECT
    id
  FROM
    hier
  WHERE
    id = 5300
UNION ALL
  SELECT
    hier.id
  FROM
    T
  JOIN
    hier
      ON hier.pid = T.id
)
TABLE T;

PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข
[ืงื•ืง ืื•ื™ืฃ explain.tensor.ru]

ื•ื•ื™ ื“ืขืจื•ื•ืึทืจื˜, ืžื™ืจ ื’ืขืคึฟื•ื ืขืŸ ืึทืœืข 30 ืจืขืงืึธืจื“ืก. ืื‘ืขืจ ื–ื™ื™ ืคืืจื‘ืจืื›ื˜ 60% ืคื•ืŸ ื“ื™ ื’ืึทื ืฅ ืฆื™ื™ื˜ ืื•ื™ืฃ ื“ืขื - ื•ื•ื™ื™ึทืœ ื–ื™ื™ ืื•ื™ืš ื“ื•ืจื›ื’ืขืงืึธื›ื˜ 30 ืึธื ืคึฟืจืขื’ืŸ ืื™ืŸ ื“ื™ ืื™ื ื“ืขืงืก. ืื™ื– ืขืก ืžืขื’ืœืขืš ืฆื• ื˜ืึธืŸ ื•ื•ื™ื™ื ื™ืงืขืจ?

ืคืึทืจื ืขื ืคึผืจื•ืจืขืึทื“ื™ื ื’ ื“ื•ืจืš ืื™ื ื“ืขืงืก

ืฆื™ ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืžืึทื›ืŸ ืึท ื‘ืึทื–ื•ื ื“ืขืจ ืื™ื ื“ืขืงืก ืึธื ืคึฟืจืขื’ ืคึฟืึทืจ ื™ืขื“ืขืจ ื ืึธื“ืข? ืขืก ื˜ื•ืจื ืก ืื•ื™ืก ืึทื– ื ื™ื˜ - ืžื™ืจ ืงืขื ืขืŸ ืœื™ื™ืขื ืขืŸ ืคื•ืŸ ื“ื™ ืื™ื ื“ืขืงืก ื ื™ืฆืŸ ืขื˜ืœืขื›ืข ืฉืœื™ืกืœืขืŸ ืื™ืŸ ืึทืžืึธืœ ืื™ืŸ ืื™ื™ืŸ ืจื•ืคืŸ ื“ื•ืจืš = ANY(array).

ืื•ืŸ ืื™ืŸ ื™ืขื“ืขืจ ืึทื–ืึท ื’ืจื•ืคึผืข ืคื•ืŸ โ€‹โ€‹ื™ื“ืขื ื˜ื™ืคื™ืขืจืก ืžื™ืจ ืงืขื ืขืŸ ื ืขืžืขืŸ ืึทืœืข ื“ื™ IDs ื’ืขืคึฟื•ื ืขืŸ ืื™ืŸ ื“ื™ ืคืจื™ืขืจื“ื™ืงืข ืฉืจื™ื˜ ื“ื•ืจืš "ื ืึธื“ืขืก". ืึทื– ืื™ื–, ืื™ืŸ ื™ืขื“ืขืจ ื•ื•ื™ื™ึทื˜ืขืจ ืฉืจื™ื˜ ืžื™ืจ ื•ื•ืขืœืŸ ื–ื•ื›ืŸ ืคึฟืึทืจ ืึทืœืข ืงื™ื ื“ืกืงื™ื ื“ืขืจ ืคื•ืŸ ืึท ื–ื™ื›ืขืจ ืžื“ืจื’ื” ืื™ืŸ ืึทืžืึธืœ.

ื ืึธืจ ื“ืึธ ืื™ื– ื“ื™ ืคึผืจืึธื‘ืœืขื, ืื™ืŸ ืจืขืงื•ืจืกื™ื•ื•ืข ืกืขืœืขืงืฆื™ืข, ืื™ืจ ืงืขื ืขืŸ ื ื™ืฉื˜ ืึทืงืกืขืก ื–ื™ืš ืื™ืŸ ืึท ื ืขืกื˜ืขื“ ืึธื ืคึฟืจืขื’, ืื‘ืขืจ ืžื™ืจ ื“ืืจืคืŸ ืขืคืขืก ืื•ื™ืกืงืœื™ื™ื‘ืŸ ื ืืจ ื•ื•ืืก ืžืขืŸ ื”ืื˜ ื’ืขืคื•ื ืขืŸ ืื•ื™ืคืŸ ืคืจื™ืขืจื“ื™ื’ืŸ ืฉื˜ืืคืœ... ืขืก ืงื•ืžื˜ ืื•ื™ืก ืื– ืขืก ืื™ื– ืื•ืžืžืขื’ืœื™ืš ืฆื• ืžืื›ืŸ ื ื ืขืกื˜ืขื“ ืื ืคืจืื’ืข ืคืืจ ื“ื™ ื’ืื ืฆืข ืกืขืœืขืงืฆื™ืข, ืื‘ืขืจ ืคืืจ ืื™ืจ ืกืคืขืฆื™ืคื™ืฉืŸ ืคืขืœื“ ืื™ื– ืขืก ืžืขื’ืœื™ืš. ืื•ืŸ ื“ืึธืก ืคืขืœื“ ืงืขืŸ ืื•ื™ืš ื–ื™ื™ืŸ ืึท ืžืขื ื’ืข - ื•ื•ืึธืก ืื™ื– ื•ื•ืึธืก ืžื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื ื•ืฆืŸ ANY.

ืขืก ืกืึธื•ื ื“ืก ืึท ื‘ื™ืกืœ ืžืฉื•ื’ืข, ืึธื‘ืขืจ ืื™ืŸ ื“ื™ ื“ื™ืึทื’ืจืึทืžืข ืึทืœืฅ ืื™ื– ืคึผืฉื•ื˜.

PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข

WITH RECURSIVE T AS (
  SELECT
    ARRAY[id] id$
  FROM
    hier
  WHERE
    id = 5300
UNION ALL
  SELECT
    ARRAY(
      SELECT
        id
      FROM
        hier
      WHERE
        pid = ANY(T.id$)
    ) id$
  FROM
    T
  WHERE
    coalesce(id$, '{}') <> '{}' -- ัƒัะปะพะฒะธะต ะฒั‹ั…ะพะดะฐ ะธะท ั†ะธะบะปะฐ - ะฟัƒัั‚ะพะน ะผะฐััะธะฒ
)
SELECT
  unnest(id$) id
FROM
  T;

PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข
[ืงื•ืง ืื•ื™ืฃ explain.tensor.ru]

ืื•ืŸ ื“ืึธ ื“ื™ ืžืขืจืกื˜ ื•ื•ื™ื›ื˜ื™ืง ื–ืึทืš ืื™ื– ื ื™ืฉื˜ ืึทืคึฟื™ืœื• ื’ืขื•ื•ื™ื ืขืŸ 1.5 ืžืืœ ืื™ืŸ ืฆื™ื™ื˜ืื•ืŸ ืึทื– ืžื™ืจ ื”ืึธื‘ืŸ ืึทืจืึธืคึผื’ืขื ื•ืžืขืŸ ื•ื•ื™ื™ื ื™ืงืขืจืข ื‘ืึทืคืขืจืก, ื•ื•ื™ื™ึทืœ ืžื™ืจ ื”ืึธื‘ืŸ ื‘ืœื•ื™ื– 5 ืจื•ืคื˜ ืฆื• ื“ื™ ืื™ื ื“ืขืงืก ืึทื ืฉื˜ืึธื˜ ืคื•ืŸ 30!

ืึทืŸ ื ืึธืš ื‘ืึธื ื•ืก ืื™ื– ื“ืขืจ ืคืึทืงื˜ ืึทื– ื ืึธืš ื“ื™ ืœืขืฆื˜ ื•ืžื ืึทืกื˜, ื“ื™ ื™ื“ืขื ื˜ื™ืคื™ืขืจืก ื•ื•ืขืœืŸ ื‘ืœื™ื™ื‘ืŸ ืึธืจื“ืขืจื“ ื“ื•ืจืš "ืœืขื•ื•ืขืœืก".

ื ืึธื“ืข ืฆื™ื™ื›ืŸ

ื“ืขืจ ื•ื•ื™ื™ึทื˜ืขืจ ื‘ืึทื˜ืจืึทื›ื˜ื•ื ื’ ื•ื•ืึธืก ื•ื•ืขื˜ ื”ืขืœืคึฟืŸ ืคึฟืึทืจื‘ืขืกืขืจืŸ ืคืึธืจืฉื˜ืขืœื•ื ื’ ืื™ื– - "ื‘ืœืขื˜ืขืจ" ืงืขื ืขืŸ ื ื™ืฉื˜ ื”ืึธื‘ืŸ ืงื™ื ื“ืขืจ, ื“ืึธืก ื”ื™ื™ืกื˜, ืคืึทืจ ื–ื™ื™ ื“ืึทืจืฃ ืžืขืŸ ื’ืึธืจ ื ื™ื˜ ืงื•ืงืŸ โ€œืึทืจืึธืคึผโ€. ืื™ืŸ ื“ืขืจ ืคืึธืจืžื™ืจื•ื ื’ ืคื•ืŸ ืื•ื ื“ื–ืขืจ ืึทืจื‘ืขื˜, ื“ืึธืก ืžื™ื˜ืœ ืึทื– ืื•ื™ื‘ ืžื™ืจ ื ืึธื›ืคืึธืœื’ืŸ ื“ื™ ืงื™ื™ื˜ ืคื•ืŸ ื“ื™ืคึผืึทืจื˜ืžืึทื ืฅ ืื•ืŸ ื“ืขืจื’ืจื™ื™ื›ืŸ ืึทืŸ ืึธื ื’ืขืฉื˜ืขืœื˜ืขืจ, ืขืก ืื™ื– ื ื™ื˜ ื“ืึทืจืคึฟืŸ ืฆื• ืงื•ืงืŸ ื•ื•ื™ื™ึทื˜ืขืจ ืฆื•ื–ืืžืขืŸ ื“ืขื ืฆื•ื•ื™ื™ึทื’.

ืœืึธืžื™ืจ ืึทืจืฒึทืŸ ืื™ืŸ ืื•ื ื“ื–ืขืจ ื˜ื™ืฉ ื ืึธืš boolean-ืคืขืœื“, ื•ื•ืึธืก ื•ื•ืขื˜ ืื•ื ื“ื– ื’ืœื™ื™ืš ื–ืึธื’ืŸ ืฆื™ ื“ืขืจ ื‘ืึทื–ื•ื ื“ืขืจ ืคึผืึธื–ื™ืฆื™ืข ืื™ืŸ ืื•ื ื“ื–ืขืจ ื‘ื•ื™ื ืื™ื– ืึท "ื ืึธื“ืข" - ื“ืึธืก ืื™ื–, ืฆื™ ืขืก ืงืขืŸ ื”ืึธื‘ืŸ ืงื™ื ื“ืกืงื™ื ื“ืขืจ ืื™ืŸ ืึทืœืข.

ALTER TABLE hier
  ADD COLUMN branch boolean;

UPDATE
  hier T
SET
  branch = TRUE
WHERE
  EXISTS(
    SELECT
      NULL
    FROM
      hier
    WHERE
      pid = T.id
    LIMIT 1
);
-- ะ—ะฐะฟั€ะพั ัƒัะฟะตัˆะฝะพ ะฒั‹ะฟะพะปะฝะตะฝ: 3033 ัั‚ั€ะพะบ ะธะทะผะตะฝะตะฝะพ ะทะฐ 42 ะผั.

ื’ืจื•ื™ืก! ืขืก ื˜ื•ืจื ืก ืื•ื™ืก ืึทื– ื‘ืœื•ื™ื– ืึท ื‘ื™ืกืœ ืžืขืจ ื•ื•ื™ 30% ืคื•ืŸ ืึทืœืข ื‘ื•ื™ื ืขืœืขืžืขื ื˜ืŸ ื”ืึธื‘ืŸ ืงื™ื ื“ืกืงื™ื ื“ืขืจ.

ืื™ืฆื˜ ืœืึธื–ืŸ ืก ื ื•ืฆืŸ ืึท ื‘ื™ืกืœ ืึทื ื“ืขืจืฉ ืžืขื›ืึทื ื™ืงืขืจ - ืงืึทื ืขืงืฉืึทื ื– ืฆื• ื“ื™ ืจืขืงื•ืจืกื™ื•ื•ืข ื˜ื™ื™ืœ ื“ื•ืจืš LATERAL, ื•ื•ืึธืก ื•ื•ืขื˜ ืœืึธื–ืŸ ืื•ื ื“ื– ื’ืœื™ื™ืš ืึทืงืกืขืก ื“ื™ ืคืขืœื“ืขืจ ืคื•ืŸ ื“ื™ ืจืขืงื•ืจืกื™ื•ื•ืข "ื˜ื™ืฉ" ืื•ืŸ ื ื•ืฆืŸ ืึท ื’ืขืžื™ื™ื ื–ืึทื ืคื•ื ืงืฆื™ืข ืžื™ื˜ ืึท ืคึฟื™ืœื˜ืจื™ืจื•ื ื’ ืฆื•ืฉื˜ืึทื ื“ ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ืึท ื ืึธื“ืข ืฆื• ืจืขื“ื•ืฆื™ืจืŸ ื“ื™ ื’ืึทื ื’ ืคื•ืŸ ืฉืœื™ืกืœืขืŸ:

PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข

WITH RECURSIVE T AS (
  SELECT
    array_agg(id) id$
  , array_agg(id) FILTER(WHERE branch) ns$
  FROM
    hier
  WHERE
    id = 5300
UNION ALL
  SELECT
    X.*
  FROM
    T
  JOIN LATERAL (
    SELECT
      array_agg(id) id$
    , array_agg(id) FILTER(WHERE branch) ns$
    FROM
      hier
    WHERE
      pid = ANY(T.ns$)
  ) X
    ON coalesce(T.ns$, '{}') <> '{}'
)
SELECT
  unnest(id$) id
FROM
  T;

PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข
[ืงื•ืง ืื•ื™ืฃ explain.tensor.ru]

ืžื™ืจ ื–ืขื ืขืŸ ื‘ื™ื›ื•ืœืช ืฆื• ืจืขื“ื•ืฆื™ืจืŸ ืื™ื™ื ืขืจ ืžืขืจ ืื™ื ื“ืขืงืก ืจื•ืคืŸ ืื•ืŸ ื•ื•ืึทืŸ ืžืขืจ ื•ื•ื™ 2 ืžืืœ ืื™ืŸ ื‘ืึทื ื“ ืงืึธืจืขืงื˜ืึธืจ.

#2. ืœืืžื™ืจ ืฆื•ืจื™ืง ื’ื™ื™ืŸ ืฆื• ื“ื™ ื•ื•ืืจืฆืœืขืŸ

ื“ืขืจ ืึทืœื’ืขืจื™ื“ืึทื ื•ื•ืขื˜ ื–ื™ื™ืŸ ื ื•ืฆื™ืง ืื•ื™ื‘ ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื–ืึทืžืœืขืŸ ืจืขืงืึธืจื“ืก ืคึฟืึทืจ ืึทืœืข ืขืœืขืžืขื ื˜ืŸ "ืึทืจื•ื™ืฃ ื“ืขื ื‘ื•ื™ื", ื‘ืฉืขืช ืื™ืจ ื”ืึทืœื˜ืŸ ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ื•ื•ืึธืก ืžืงื•ืจ ื‘ืœืึทื˜ (ืื•ืŸ ืžื™ื˜ ื•ื•ืึธืก ื™ื ื“ื™ืงืึทื˜ืึธืจืก) ื’ืขืคึฟื™ืจื˜ ืขืก ืฆื• ื–ื™ื™ืŸ ืึทืจื™ื™ึทื ื’ืขืจืขื›ื ื˜ ืื™ืŸ ื“ืขืจ ืžื•ืกื˜ืขืจ - ืœืžืฉืœ, ืฆื• ื“ื–ืฉืขื ืขืจื™ื™ื˜ ืึท ืงื™ืฆืขืจ ื‘ืึทืจื™ื›ื˜. ืžื™ื˜ ืึทื’ื’ืจืขื’ืึทื˜ื™ืึธืŸ ืื™ืŸ ื ืึธื•ื“ื–.

PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข
ื“ืืก ื•ื•ืืก ืื™ื– ื•ื•ื™ื™ื˜ืขืจ ื“ืืจืฃ ืžืขืŸ ืื ื ืขืžืขืŸ ื ืืจ ืืœืก ื ื‘ืื•ื•ื™ื™ื–, ื•ื•ื™ื™ืœ ื“ื™ ื‘ืงืฉื” ืื™ื– ื–ื™ื™ืขืจ ืฉื•ื•ืขืจ. ืึธื‘ืขืจ ืื•ื™ื‘ ืขืก ื“ืึทืžืึทื ื™ื™ืฅ ื“ื™ื™ืŸ ื“ืึทื˜ืึทื‘ื™ื™ืก, ืื™ืจ ื–ืึธืœ ื˜ืจืึทื›ื˜ืŸ ื•ื•ืขื’ืŸ ื ื™ืฆืŸ ืขื ืœืขืš ื˜ืขืงื ื™ืงืก.

ื–ืืœ ืก ืึธื ื”ื™ื™ื‘ืŸ ืžื™ื˜ ืึท ืคึผืึธืจ ืคื•ืŸ ืคึผืฉื•ื˜ ืกื˜ื™ื™ื˜ืžืึทื ืฅ:

  • ื“ืขืจ ื–ืขืœื‘ื™ืงืขืจ ืจืขืงืึธืจื“ ืคื•ืŸ ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก ืขืก ืื™ื– ื‘ืขืกื˜ืขืจ ืฆื• ืœื™ื™ืขื ืขืŸ ืขืก ื ืึธืจ ืึทืžืึธืœ.
  • ืจืขืงืึธืจื“ืก ืคื•ืŸ ื“ื™ ื“ืึทื˜ืึทื‘ื™ื™ืก ืขืก ืื™ื– ืžืขืจ ืขืคืขืงื˜ื™ื•ื• ืฆื• ืœื™ื™ืขื ืขืŸ ืื™ืŸ ื‘ืึทื˜ืฉืึทื–ื•ื•ื™ ืึทืœื™ื™ืŸ.

ืื™ืฆื˜ ืœืึธืžื™ืจ ืคึผืจื•ื‘ื™ืจืŸ ืฆื• ื‘ื•ื™ืขืŸ ื“ื™ ื‘ืงืฉื” ื•ื•ืึธืก ืžื™ืจ ื“ืึทืจืคึฟืŸ.

ืฉืจื™ื˜ ืงืกื ื•ืžืงืก

ื“ืึธืš, ื•ื•ืขืŸ ื™ื ื™ื˜ื™ืึทืœื™ื–ื™ื ื’ ืจืขืงื•ืจืกื™ืึธืŸ (ื•ื•ื• ื•ื•ืึธืœื˜ ืžื™ืจ ื–ื™ื™ืŸ ืึธืŸ ืขืก!) ืžื™ืจ ื•ื•ืขืœืŸ ื”ืึธื‘ืŸ ืฆื• ืึทืจืึธืคึผืจืขื›ืขื ืขืŸ ื“ื™ ืจืขืงืึธืจื“ืก ืคื•ืŸ ื“ื™ ื‘ืœืขื˜ืขืจ ื–ื™ืš ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ื“ื™ ื’ืึทื ื’ ืคื•ืŸ ืขืจืฉื˜ ื™ื“ืขื ื˜ื™ืคื™ืขืจืก:

WITH RECURSIVE tree AS (
  SELECT
    rec -- ัั‚ะพ ั†ะตะปัŒะฝะฐั ะทะฐะฟะธััŒ ั‚ะฐะฑะปะธั†ั‹
  , id::text chld -- ัั‚ะพ "ะฝะฐะฑะพั€" ะฟั€ะธะฒะตะดัˆะธั… ััŽะดะฐ ะธัั…ะพะดะฝั‹ั… ะปะธัั‚ัŒะตะฒ
  FROM
    hier rec
  WHERE
    id = ANY('{1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192}'::integer[])
UNION ALL
  ...

ืื•ื™ื‘ ืขืžืขืฆืขืจ ื’ืขื“ืึทื ืง ืขืก ืื™ื– ืžืึธื“ื ืข ืึทื– ื“ื™ "ืฉื˜ืขืœืŸ" ืื™ื– ืกื˜ืึธืจื“ ื•ื•ื™ ืึท ืฉื˜ืจื™ืงืœ ืื•ืŸ ื ื™ืฉื˜ ืึท ืžืขื ื’ืข, ืขืก ืื™ื– ืึท ืคึผืฉื•ื˜ ื“ืขืจืงืœืขืจื•ื ื’ ืคึฟืึทืจ ื“ืขื. ืขืก ืื™ื– ืึท ื™ื ืึทื’ืจื™ื™ื˜ื™ื“ "ื’ืœื•ื™ื ื’" ืคื•ื ืงืฆื™ืข ืคึฟืึทืจ ืกื˜ืจื™ื ื’ืก string_agg, ืึธื‘ืขืจ ื ื™ืฉื˜ ืคึฟืึทืจ ืขืจื™ื™ื–. ื›ืึธื˜ืฉ ื–ื™ ื’ืจื™ื ื’ ืฆื• ื™ื ืกื˜ืจื•ืžืขื ื˜ ืื•ื™ืฃ ื“ื™ื™ืŸ ืื™ื™ื’ืŸ.

ืฉืจื™ื˜ ืงืกื ื•ืžืงืก

ืื™ืฆื˜ ืžื™ืจ ื•ื•ืึธืœื˜ ื‘ืึทืงื•ืžืขืŸ ืึท ืกื›ื•ื ืคื•ืŸ ืึธืคึผื˜ื™ื™ืœื•ื ื’ IDs ื•ื•ืึธืก ื•ื•ืขื˜ ื“ืึทืจืคึฟืŸ ืฆื• ืœื™ื™ืขื ืขืŸ ื•ื•ื™ื™ึทื˜ืขืจ. ื›ึผืžืขื˜ ืฉื˜ืขื ื“ื™ืง ื–ื™ื™ ื•ื•ืขืœืŸ ื–ื™ื™ืŸ ื“ื•ืคึผืœื™ืงื™ื™ื˜ื™ื“ ืื™ืŸ ืคืึทืจืฉื™ื“ืขื ืข ืจืขืงืึธืจื“ืก ืคื•ืŸ ื“ืขืจ ืึธืจื™ื’ื™ื ืขืœ ืฉื˜ืขืœืŸ - ืึทื–ื•ื™ ืžื™ืจ ื•ื•ืึธืœื˜ ื’ืจื•ืคึผืข ื–ื™ื™, ื‘ืฉืขืช ืคึผืจืึทื–ืขืจื•ื•ื™ื ื’ ืื™ื ืคึฟืึธืจืžืึทืฆื™ืข ื•ื•ืขื’ืŸ ื“ื™ ืžืงื•ืจ ื‘ืœืขื˜ืขืจ.

ืื‘ืขืจ ื“ื ื•ื•ืืจื˜ืŸ ืื•ื™ืฃ ืื•ื ื– ื“ืจื™ื™ ืฆืจื•ืช:

  1. ื“ืขืจ "ืกื•ื‘ืจืขืงื•ืจืกื™ื•ื•ืข" ื˜ื™ื™ืœ ืคื•ืŸ ื“ื™ ืึธื ืคึฟืจืขื’ ืงืขืŸ ื ื™ืฉื˜ ืึทื ื˜ื”ืึทืœื˜ืŸ ื’ืขืžื™ื™ื ื–ืึทื ืคืึทื ื’ืงืฉืึทื ื– ืžื™ื˜ GROUP BY.
  2. ื ืจืขืคึฟืขืจืขื ืฅ ืฆื• ืึท ืจืขืงื•ืจืกื™ื•ื•ืข "ื˜ื™ืฉ" ืงืขื ืขืŸ ื ื™ื˜ ื–ื™ื™ืŸ ืงืึทื ื˜ื™ื™ื ื“ ืื™ืŸ ืึท ื ืขืกื˜ืขื“ ืกืึทื‘ืงื•ื•ืขืจื™.
  3. ื ื‘ืงืฉื” ืื™ืŸ ื“ื™ ืจืขืงื•ืจืกื™ื•ื•ืข ื˜ื™ื™ืœ ืงืขืŸ ื ื™ืฉื˜ ืึทื ื˜ื”ืึทืœื˜ืŸ ืึท CTE.

ืฆื•ืž ื’ืœื™ืง, ืึทืœืข ื“ื™ ืคืจืื‘ืœืขืžืขืŸ ื–ืขื ืขืŸ ื’ืึทื ืฅ ื’ืจื™ื ื’ ืฆื• ืึทืจื‘ืขื˜ืŸ ืึทืจื•ื. ื–ืืœ ืก ืึธื ื”ื™ื™ื‘ืŸ ืคื•ืŸ ื“ื™ ืกื•ืฃ.

CTE ืื™ืŸ ืจืขืงื•ืจืกื™ื•ื•ืข ื˜ื™ื™ืœ

ืึทื–ื•ื™ ืงื™ื™ืŸ ืืจื‘ืขื˜ืŸ:

WITH RECURSIVE tree AS (
  ...
UNION ALL
  WITH T (...)
  SELECT ...
)

ืื•ืŸ ืึทื–ื•ื™ ืขืก ืึทืจื‘ืขื˜, ื“ื™ ืงืœืึทืžืขืจืŸ ืžืึทื›ืŸ ื“ื™ ื—ื™ืœื•ืง!

WITH RECURSIVE tree AS (
  ...
UNION ALL
  (
    WITH T (...)
    SELECT ...
  )
)

ื ืขืกื˜ืขื“ ืึธื ืคึฟืจืขื’ ืงืขื’ืŸ ืึท ืจืขืงื•ืจืกื™ื•ื•ืข "ื˜ื™ืฉ"

ื”ืžืž... ื ืจืขืงื•ืจืกื™ื•ื•ืข CTE ืงืขื ืขืŸ ื ื™ื˜ ื–ื™ื™ืŸ ืึทืงืกืขืกื˜ ืื™ืŸ ืึท ืกืึทื‘ืงื•ื•ืขืจื™. ืึธื‘ืขืจ ืขืก ืงืขืŸ ื–ื™ื™ืŸ ืื™ืŸ CTE! ืื•ืŸ ืึท ื ืขืกื˜ืขื“ ื‘ืขื˜ืŸ ืงืขื ืขืŸ ืฉื•ื™ืŸ ืึทืงืกืขืก ื“ืขื CTE!

GROUP BY ื™ืŸ ืจืขืงื•ืจืกื™ืึธืŸ

ืขืก ืื™ื– ืคึผืจื™ืงืจืข, ืึธื‘ืขืจ ... ืžื™ืจ ื”ืึธื‘ืŸ ืึท ืคึผืฉื•ื˜ ื•ื•ืขื’ ืฆื• ืขืžื™ืึทืœื™ื™ื˜ GROUP BY ื ื™ืฆืŸ DISTINCT ON ืื•ืŸ ืคึฟืขื ืฆื˜ืขืจ ืคืึทื ื’ืงืฉืึทื ื–!

SELECT
  (rec).pid id
, string_agg(chld::text, ',') chld
FROM
  tree
WHERE
  (rec).pid IS NOT NULL
GROUP BY 1 -- ะฝะต ั€ะฐะฑะพั‚ะฐะตั‚!

ืื•ืŸ ื“ืึธืก ืื™ื– ื•ื•ื™ ืขืก ืึทืจื‘ืขื˜!

SELECT DISTINCT ON((rec).pid)
  (rec).pid id
, string_agg(chld::text, ',') OVER(PARTITION BY (rec).pid) chld
FROM
  tree
WHERE
  (rec).pid IS NOT NULL

ืื™ืฆื˜ ืžื™ืจ ื–ืขืŸ ื•ื•ืึธืก ื“ื™ ื ื•ืžืขืจื™ืง ืฉื™ื™ึทืŸ ืื™ื– ืคืืจื•ื•ืื ื“ืœืขืŸ ืื™ืŸ ื˜ืขืงืกื˜ - ืึทื–ื•ื™ ืึทื– ื–ื™ื™ ืงืขื ืขืŸ ื–ื™ื™ืŸ ื“ื–ืฉื•ื™ื ื“ ืฆื•ื–ืึทืžืขืŸ ืืคื’ืขืฉื™ื™ื“ื˜ ื“ื•ืจืš ืงืึธืžืžืึทืก!

ืฉืจื™ื˜ ืงืกื ื•ืžืงืก

ืคึฟืึทืจ ื“ื™ ืœืขืฆื˜ ืžื™ืจ ื”ืึธื‘ืŸ ื’ืึธืจื ื™ืฉื˜ ืœื™ื ืงืก:

  • ืžื™ืจ ืœื™ื™ืขื ืขืŸ "ืึธืคึผื˜ื™ื™ืœื•ื ื’" ืจืขืงืึธืจื“ืก ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ืึท ื’ืึทื ื’ ืคื•ืŸ ื’ืจื•ืคึผื˜ IDs
  • ืžื™ืจ ืคืึทืจื’ืœื™ื™ึทื›ืŸ ื“ื™ ืกืึทื‘ื˜ืจืึทืงื˜ื™ื“ ืกืขืงืฉืึทื ื– ืžื™ื˜ ื“ื™ "ืกืขืฅ" ืคื•ืŸ ื“ืขืจ ืึธืจื™ื’ื™ื ืขืœ ืฉื™ืฅ
  • "ื‘ืจื™ื™ื˜ืขืจืŸ" ื“ื™ ืฉื˜ืขืœืŸ-ืฉื˜ืจื™ืงืœ ื ื™ืฆืŸ unnest(string_to_array(chld, ',')::integer[])

WITH RECURSIVE tree AS (
  SELECT
    rec
  , id::text chld
  FROM
    hier rec
  WHERE
    id = ANY('{1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192}'::integer[])
UNION ALL
  (
    WITH prnt AS (
      SELECT DISTINCT ON((rec).pid)
        (rec).pid id
      , string_agg(chld::text, ',') OVER(PARTITION BY (rec).pid) chld
      FROM
        tree
      WHERE
        (rec).pid IS NOT NULL
    )
    , nodes AS (
      SELECT
        rec
      FROM
        hier rec
      WHERE
        id = ANY(ARRAY(
          SELECT
            id
          FROM
            prnt
        ))
    )
    SELECT
      nodes.rec
    , prnt.chld
    FROM
      prnt
    JOIN
      nodes
        ON (nodes.rec).id = prnt.id
  )
)
SELECT
  unnest(string_to_array(chld, ',')::integer[]) leaf
, (rec).*
FROM
  tree;

PostgreSQL ืึทื ื˜ื™ืคึผืึทื˜ื˜ืขืจื ืก: ื•ื•ื™ ื˜ื™ืฃ ืื™ื– ื“ื™ ืงื™ื ื™ื’ืœ ืœืึธืš? ืœืึธืžื™ืจ ื“ื•ืจื›ื’ื™ื™ืŸ ื“ื™ ื›ื™ื™ืขืจืึทืจืงื™ืข
[ืงื•ืง ืื•ื™ืฃ explain.tensor.ru]

ืžืงื•ืจ: www.habr.com

ืœื™ื™ื’ืŸ ืึท ื‘ืึทืžืขืจืงื•ื ื’