PostgreSQL เชเชจเซเชŸเชฟเชชเซ‡เชŸเชฐเซเชจ: เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเชพ เชŠเช‚เชกเชพ เช›เซ‡? เชšเชพเชฒเซ‹ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชชเชธเชพเชฐ เช•เชฐเซ€เช

เชœเชŸเชฟเชฒ ERP เชธเชฟเชธเซเชŸเชฎเซ‹เชฎเชพเช‚ เช˜เชฃเซ€ เชธเช‚เชธเซเชฅเชพเช“ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชธเซเชตเชญเชพเชต เชงเชฐเชพเชตเซ‡ เช›เซ‡เชœเซเชฏเชพเชฐเซ‡ เชธเชœเชพเชคเซ€เชฏ เชตเชธเซเชคเซเช“ เช…เช‚เชฆเชฐ เช†เชตเซ‡ เช›เซ‡ เชชเซ‚เชฐเซเชตเชœ-เชตเช‚เชถเชœ เชธเช‚เชฌเช‚เชงเซ‹เชจเซเช‚ เชตเซƒเช•เซเชท - เช† เชเชจเซเชŸเชฐเชชเซเชฐเชพเช‡เชเชจเซเช‚ เชธเช‚เช—เช เชจเชพเชคเซเชฎเช• เชฎเชพเชณเช–เซเช‚ เช›เซ‡ (เช† เชคเชฎเชพเชฎ เชถเชพเช–เชพเช“, เชตเชฟเชญเชพเช—เซ‹ เช…เชจเซ‡ เช•เชพเชฐเซเชฏ เชœเซ‚เชฅเซ‹), เช…เชจเซ‡ เชฎเชพเชฒเชจเซ€ เชธเซ‚เชšเชฟ, เช…เชจเซ‡ เช•เชพเชฐเซเชฏเชจเชพ เช•เซเชทเซ‡เชคเซเชฐเซ‹, เช…เชจเซ‡ เชตเซ‡เชšเชพเชฃ เชฌเชฟเช‚เชฆเซเช“เชจเซ€ เชญเซ‚เช—เซ‹เชณ,...

PostgreSQL เชเชจเซเชŸเชฟเชชเซ‡เชŸเชฐเซเชจ: เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเชพ เชŠเช‚เชกเชพ เช›เซ‡? เชšเชพเชฒเซ‹ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชชเชธเชพเชฐ เช•เชฐเซ€เช

เชนเช•เซ€เช•เชคเชฎเชพเช‚, เชคเซเชฏเชพเช‚ เช•เซ‹เชˆ เชจเชฅเซ€ เชฌเชฟเชเชจเซ‡เชธ เช“เชŸเซ‹เชฎเซ‡เชถเชจ เชตเชฟเชธเซเชคเชพเชฐเซ‹, เชœเซเชฏเชพเช‚ เชชเชฐเชฟเชฃเชพเชฎเซ‡ เช•เซ‹เชˆ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชจเชนเซ€เช‚ เชนเซ‹เชฏ. เชชเชฐเช‚เชคเซ เชœเซ‹ เชคเชฎเซ‡ "เชตเซเชฏเชตเชธเชพเชฏ เชฎเชพเชŸเซ‡" เช•เชพเชฎ เชจ เช•เชฐเซ‹ เชคเซ‹ เชชเชฃ, เชคเชฎเซ‡ เชนเชœเซ€ เชชเชฃ เชธเชฐเชณเชคเชพเชฅเซ€ เชถเซเชฐเซ‡เชฃเซ€เชฌเชฆเซเชง เชธเช‚เชฌเช‚เชงเซ‹เชจเซ‹ เชธเชพเชฎเชจเซ‹ เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹. เชคเซ‡ เชŸเซเชฐเซ€เชŸ เช›เซ‡, เชคเชฎเชพเชฐเชพ เชซเซ‡เชฎเชฟเชฒเซ€ เชŸเซเชฐเซ€ เช…เชฅเชตเชพ เชถเซ‹เชชเชฟเช‚เช— เชธเซ‡เชจเซเชŸเชฐเชฎเชพเช‚ เชชเชฐเชฟเชธเชฐเชจเซ€ เชซเซเชฒเซ‹เชฐ เชชเซเชฒเชพเชจ เชชเชฃ เชธเชฎเชพเชจ เชฐเชšเชจเชพ เช›เซ‡.

เชกเซ€เชฌเซ€เชเชฎเชเชธเชฎเชพเช‚ เช†เชตเชพ เชตเซƒเช•เซเชทเชจเซ‡ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเชตเชพเชจเซ€ เช˜เชฃเซ€ เชฐเซ€เชคเซ‹ เช›เซ‡, เชชเชฐเช‚เชคเซ เช†เชœเซ‡ เช†เชชเชฃเซ‡ เชซเช•เซเชค เชเช• เชตเชฟเช•เชฒเซเชช เชชเชฐ เชงเซเชฏเชพเชจ เช•เซ‡เชจเซเชฆเซเชฐเชฟเชค เช•เชฐเซ€เชถเซเช‚:

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

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

เช…เชจเซ‡ เชœเซเชฏเชพเชฐเซ‡ เชคเชฎเซ‡ เชชเชฆเชพเชจเซเช•เซเชฐเชฎเชจเชพ เชŠเช‚เชกเชพเชฃเชฎเชพเช‚ เชกเซ‹เช•เชฟเชฏเซเช‚ เช•เชฐเซ€ เชฐเชนเซเชฏเชพ เช›เซ‹, เชคเซเชฏเชพเชฐเซ‡ เชคเซ‡ เชงเซ€เชฐเชœเชชเซ‚เชฐเซเชตเช• เช เชœเซ‹เชตเชพเชจเซ€ เชฐเชพเชน เชœเซ‹เชˆ เชฐเชนเซเชฏเซเช‚ เช›เซ‡ เช•เซ‡ เช†เชตเซ€ เชฐเชšเชจเชพ เชธเชพเชฅเซ‡ เช•เชพเชฎ เช•เชฐเชตเชพเชจเซ€ เชคเชฎเชพเชฐเซ€ "เชจเชฟเชทเซเช•เชชเชŸ" เชฐเซ€เชคเซ‹ เช•เซ‡เชŸเชฒเซ€ [เชฎเชพเช‚] เช…เชธเชฐเช•เชพเชฐเช• เชฐเชนเซ‡เชถเซ‡.

PostgreSQL เชเชจเซเชŸเชฟเชชเซ‡เชŸเชฐเซเชจ: เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเชพ เชŠเช‚เชกเชพ เช›เซ‡? เชšเชพเชฒเซ‹ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชชเชธเชพเชฐ เช•เชฐเซ€เช
เชšเชพเชฒเซ‹ เชธเชพเชฎเชพเชจเซเชฏ เชธเชฎเชธเซเชฏเชพเช“ เช•เซ‡ เชœเซ‡ เช‰เชฆเซเชฆเชญเชตเซ‡ เช›เซ‡, เชคเซ‡เช“เชจเซเช‚ SQL เชฎเชพเช‚ เช…เชฎเชฒเซ€เช•เชฐเชฃ เชœเซ‹เชˆเช เช…เชจเซ‡ เชคเซ‡เชฎเชจเซเช‚ เชชเซเชฐเชฆเชฐเซเชถเชจ เชธเซเชงเชพเชฐเชตเชพเชจเซ‹ เชชเซเชฐเชฏเชพเชธ เช•เชฐเซ€เช.

#1. เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเซเช‚ เชŠเช‚เชกเซเช‚ เช›เซ‡?

เชšเชพเชฒเซ‹, เชจเชฟเชถเซเชšเชฟเชคเชคเชพ เชฎเชพเชŸเซ‡, เชธเซเชตเซ€เช•เชพเชฐเซ€เช เช•เซ‡ เช† เชฎเชพเชณเช–เซเช‚ เชธเช‚เชธเซเชฅเชพเชจเชพ เชฎเชพเชณเช–เชพเชฎเชพเช‚ เชตเชฟเชญเชพเช—เซ‹เชจเซ€ เช—เซŒเชฃเชคเชพเชจเซ‡ เชชเซเชฐเชคเชฟเชฌเชฟเช‚เชฌเชฟเชค เช•เชฐเชถเซ‡: เชตเชฟเชญเชพเช—เซ‹, เชตเชฟเชญเชพเช—เซ‹, เช•เซเชทเซ‡เชคเซเชฐเซ‹, เชถเชพเช–เชพเช“, เช•เชพเชฐเซเชฏเช•เชพเชฐเซ€ เชœเซ‚เชฅเซ‹... - เชคเชฎเซ‡ เชคเซ‡เชฎเชจเซ‡ เชœเซ‡ เชชเชฃ เช•เชนเซ‹ เช›เซ‹.
PostgreSQL เชเชจเซเชŸเชฟเชชเซ‡เชŸเชฐเซเชจ: เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเชพ เชŠเช‚เชกเชพ เช›เซ‡? เชšเชพเชฒเซ‹ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชชเชธเชพเชฐ เช•เชฐเซ€เช

เชชเซเชฐเชฅเชฎ, เชšเชพเชฒเซ‹ เช†เชชเชฃเชพ 10K เชคเชคเซเชตเซ‹เชจเซเช‚ 'เชŸเซเชฐเซ€' เชœเชจเชฐเซ‡เชŸ เช•เชฐเซ€เช

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;

เชšเชพเชฒเซ‹ เชธเซŒเชฅเซ€ เชธเชฐเชณ เช•เชพเชฐเซเชฏเชฅเซ€ เชถเชฐเซ‚เช†เชค เช•เชฐเซ€เช - เชšเซ‹เช•เซเช•เชธ เชธเซ‡เช•เซเชŸเชฐเชฎเชพเช‚ เช…เชฅเชตเชพ เชตเช‚เชถเชตเซ‡เชฒเชพเชจเซ€ เชฆเซเชฐเชทเซเชŸเชฟเช เช•เชพเชฎ เช•เชฐเชคเชพ เชคเชฎเชพเชฎ เช•เชฐเซเชฎเชšเชพเชฐเซ€เช“เชจเซ‡ เชถเซ‹เชงเชตเชพ - เชจเซ‹เชกเชจเชพ เชคเชฎเชพเชฎ เชฌเชพเชณเช•เซ‹เชจเซ‡ เชถเซ‹เชงเซ‹. เชตเช‚เชถเชœเชจเซ€ "เชŠเช‚เชกเชพเชˆ" เชฎเซ‡เชณเชตเชตเซ€ เชชเชฃ เชธเชฐเชธ เชฐเชนเซ‡เชถเซ‡... เช† เชฌเชงเซเช‚ เชœเชฐเซ‚เชฐเซ€ เชนเซ‹เชˆ เชถเช•เซ‡ เช›เซ‡, เช‰เชฆเชพเชนเชฐเชฃ เชคเชฐเซ€เช•เซ‡, เช•เซ‹เชˆ เชชเซเชฐเช•เชพเชฐเชจเซเช‚ เชจเชฟเชฐเซเชฎเชพเชฃ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เช† เช•เชฐเซเชฎเชšเชพเชฐเซ€เช“เชจเชพ ID เชจเซ€ เชฏเชพเชฆเซ€เชจเชพ เช†เชงเชพเชฐเซ‡ เชœเชŸเชฟเชฒ เชชเชธเช‚เชฆเช—เซ€.

เชœเซ‹ เช† เชตเช‚เชถเชœเซ‹เชจเชพ เชฎเชพเชคเซเชฐ เชฌเซ‡ เชธเซเชคเชฐเซ‹ เชนเซ‹เชฏ เช…เชจเซ‡ เชธเช‚เช–เซเชฏเชพ เชเช• เชกเชเชจเชจเซ€ เช…เช‚เชฆเชฐ เชนเซ‹เชฏ เชคเซ‹ เชฌเชงเซเช‚ เชธเชพเชฐเซเช‚ เชฐเชนเซ‡เชถเซ‡, เชชเชฐเช‚เชคเซ เชœเซ‹ เชคเซเชฏเชพเช‚ 5 เชฅเซ€ เชตเชงเซ เชธเซเชคเชฐเซ‹ เช›เซ‡, เช…เชจเซ‡ เชชเชนเซ‡เชฒเซ‡เชฅเซ€ เชœ เชกเชเชจเซ‡เช• เชตเช‚เชถเชœเซ‹ เช›เซ‡, เชคเซ‹ เชคเซเชฏเชพเช‚ เชธเชฎเชธเซเชฏเชพเช“ เชนเซ‹เชˆ เชถเช•เซ‡ เช›เซ‡. เชšเชพเชฒเซ‹ เชœเซ‹เชˆเช เช•เซ‡ เชชเชฐเช‚เชชเชฐเชพเช—เชค เชกเชพเช‰เชจ-เชŸเซเชฐเซ€ เชถเซ‹เชง เชตเชฟเช•เชฒเซเชชเซ‹ เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชฒเช–เชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ (เช…เชจเซ‡ เช•เชพเชฐเซเชฏ). เชชเชฐเช‚เชคเซ เชชเซเชฐเชฅเชฎ, เชšเชพเชฒเซ‹ เชจเช•เซเช•เซ€ เช•เชฐเซ€เช เช•เซ‡ เช…เชฎเชพเชฐเชพ เชธเช‚เชถเซ‹เชงเชจ เชฎเชพเชŸเซ‡ เช•เชฏเชพ เช—เชพเช‚เช เซ‹ เชธเซŒเชฅเซ€ เชตเชงเซ เชฐเชธเชชเซเชฐเชฆ เชฐเชนเซ‡เชถเซ‡.

เชธเซŒเชฅเซ€ เชตเชงเซ "เชŠเช‚เชกเชพ" เชชเซ‡เชŸเชพ เชตเซƒเช•เซเชทเซ‹:

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}
...

เชธเซŒเชฅเซ€ เชตเชงเซ "เชตเชฟเชถเชพเชณ" เชชเซ‡เชŸเชพ เชตเซƒเช•เซเชทเซ‹:

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

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

เช† เชชเซเชฐเชถเซเชจเซ‹ เชฎเชพเชŸเซ‡ เช…เชฎเซ‡ เชฒเชพเช•เซเชทเชฃเชฟเช•เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซเชฏเซ‹ เชชเซเชจเชฐเชพเชตเชฐเซเชคเชฟเชค เชœเซ‹เชกเชพเช“:
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 เชเชจเซเชŸเชฟเชชเซ‡เชŸเชฐเซเชจ: เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเชพ เชŠเช‚เชกเชพ เช›เซ‡? เชšเชพเชฒเซ‹ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชชเชธเชพเชฐ เช•เชฐเซ€เช
[explan.tensor.ru เชชเชฐ เชœเซเช“]

เช…เชชเซ‡เช•เซเชทเชพ เชฎเซเชœเชฌ, เช…เชฎเชจเซ‡ เชคเชฎเชพเชฎ 30 เชฐเซ‡เช•เซ‹เชฐเซเชก เชฎเชณเซเชฏเชพ. เชชเชฐเช‚เชคเซ เชคเซ‡เช“เช เช†เชจเชพ เชชเชฐ เช•เซเชฒ เชธเชฎเชฏเชจเซ‹ 60% เช–เชฐเซเชš เช•เชฐเซเชฏเซ‹ - เช•เชพเชฐเชฃ เช•เซ‡ เชคเซ‡เช“เช เช…เชจเซเช•เซเชฐเชฎเชฃเชฟเช•เชพเชฎเชพเช‚ 30 เชถเซ‹เชง เชชเชฃ เช•เชฐเซ€. เชถเซเช‚ เช“เช›เซเช‚ เช•เชฐเชตเซเช‚ เชถเช•เซเชฏ เช›เซ‡?

เช‡เชจเซเชกเซ‡เช•เซเชธ เชฆเซเชตเชพเชฐเชพ เชฌเชฒเซเช• เชชเซเชฐเซ‚เชซเชฐเซ€เชกเชฟเช‚เช—

เชถเซเช‚ เช†เชชเชฃเซ‡ เชฆเชฐเซ‡เช• เชจเซ‹เชก เชฎเชพเชŸเซ‡ เช…เชฒเช— เช‡เชจเซเชกเซ‡เช•เซเชธ เช•เซเชตเซ‡เชฐเซ€ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡? เชคเซ‡ เชคเชพเชฐเชฃ เช†เชชเซ‡ เช›เซ‡ เช•เซ‡ เชจเชพ - เช…เชฎเซ‡ เช…เชจเซเช•เซเชฐเชฎเชฃเชฟเช•เชพเชฎเชพเช‚เชฅเซ€ เชตเชพเช‚เชšเซ€ เชถเช•เซ€เช เช›เซ€เช เชเช• เช•เซ‰เชฒเชฎเชพเช‚ เชเช• เชธเชพเชฅเซ‡ เช…เชจเซ‡เช• เช•เซ€เชจเซ‹ เช‰เชชเชฏเซ‹เช— เชจเซ€ เชฎเชฆเชฆ เชธเชพเชฅเซ‡ = ANY(array).

เช…เชจเซ‡ เช“เชณเช–เช•เชฐเซเชคเชพเช“เชจเชพ เช†เชตเชพ เชฆเชฐเซ‡เช• เชœเซ‚เชฅเชฎเชพเช‚ เช†เชชเชฃเซ‡ เช…เช—เชพเช‰เชจเชพ เชชเช—เชฒเชพเชฎเชพเช‚ เชฎเชณเซ‡เชฒ เชคเชฎเชพเชฎ ID เชจเซ‡ โ€œเชจเซ‹เชกเซเชธโ€ เชฆเซเชตเชพเชฐเชพ เชฒเชˆ เชถเช•เซ€เช เช›เซ€เช. เชเชŸเชฒเซ‡ เช•เซ‡, เชฆเชฐเซ‡เช• เช†เช—เชฒเชพ เชชเช—เชฒเชพ เชชเชฐ เช†เชชเชฃเซ‡ เช•เชฐเซ€เชถเซเช‚ เชเช• เชœ เชธเชฎเชฏเซ‡ เชšเซ‹เช•เซเช•เชธ เชธเซเชคเชฐเชจเชพ เชคเชฎเชพเชฎ เชตเช‚เชถเชœเซ‹ เชฎเชพเชŸเซ‡ เชถเซ‹เชงเซ‹.

เชฎเชพเชคเซเชฐ, เช…เชนเซ€เช‚ เชธเชฎเชธเซเชฏเชพ เช›เซ‡, เชชเซเชจเชฐเชพเชตเชฐเซเชคเชฟเชค เชชเชธเช‚เชฆเช—เซ€เชฎเชพเช‚, เชคเชฎเซ‡ เชจเซ‡เชธเซเชŸเซ‡เชก เช•เซเชตเซ‡เชฐเซ€เชฎเชพเช‚เชฅเซ€ เชชเซ‹เชคเชพเชจเซ‡ เชเช•เซเชธเซ‡เชธ เช•เชฐเซ€ เชถเช•เชคเชพ เชจเชฅเซ€, เชชเชฐเช‚เชคเซ เช†เชชเชฃเซ‡ เช•เซ‹เชˆเช• เชฐเซ€เชคเซ‡ เชซเช•เซเชค เชคเซ‡ เชœ เชชเชธเช‚เชฆ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ เชœเซ‡ เช…เช—เชพเช‰เชจเชพ เชธเซเชคเชฐ เชชเชฐ เชฎเชณเซ€ เชนเชคเซ€... เชคเซ‡ เชคเชพเชฐเชฃ เช†เชชเซ‡ เช›เซ‡ เช•เซ‡ เชธเชฎเช—เซเชฐ เชชเชธเช‚เชฆเช—เซ€ เชฎเชพเชŸเซ‡ เชจเซ‡เชธเซเชŸเซ‡เชก เช•เซเชตเซ‡เชฐเซ€ เช•เชฐเชตเซ€ เช…เชถเช•เซเชฏ เช›เซ‡, เชชเชฐเช‚เชคเซ เชคเซ‡เชจเชพ เชšเซ‹เช•เซเช•เชธ เช•เซเชทเซ‡เชคเซเชฐ เชฎเชพเชŸเซ‡ เชคเซ‡ เชถเช•เซเชฏ เช›เซ‡. เช…เชจเซ‡ เช† เช•เซเชทเซ‡เชคเซเชฐ เชเชฐเซ‡ เชชเชฃ เชนเซ‹เชˆ เชถเช•เซ‡ เช›เซ‡ - เชœเซ‡เชจเซ‹ เช†เชชเชฃเซ‡ เช‰เชชเชฏเซ‹เช— เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ 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 เชเชจเซเชŸเชฟเชชเซ‡เชŸเชฐเซเชจ: เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเชพ เชŠเช‚เชกเชพ เช›เซ‡? เชšเชพเชฒเซ‹ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชชเชธเชพเชฐ เช•เชฐเซ€เช
[explan.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 เชเชจเซเชŸเชฟเชชเซ‡เชŸเชฐเซเชจ: เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเชพ เชŠเช‚เชกเชพ เช›เซ‡? เชšเชพเชฒเซ‹ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชชเชธเชพเชฐ เช•เชฐเซ€เช
[explan.tensor.ru เชชเชฐ เชœเซเช“]

เช…เชฎเซ‡ เชตเชงเซ เชเช• เช‡เชจเซเชกเซ‡เช•เซเชธ เช•เซ‰เชฒ เช˜เชŸเชพเชกเชตเชพเชฎเชพเช‚ เชธเช•เซเชทเชฎ เชนเชคเชพ เช…เชจเซ‡ เชตเซ‹เชฒเซเชฏเซเชฎเชฎเชพเช‚ 2 เช•เชฐเชคเชพ เชตเชงเซ เชตเช–เชค เชœเซ€เชคเซเชฏเซ‹ เชชเซเชฐเซ‚เชซเชฐเซ€เชก

#2. เชšเชพเชฒเซ‹ เชฎเซ‚เชณ เชชเชฐ เชชเชพเช›เชพ เชœเชˆเช

เช† เชเชฒเซเช—เซ‹เชฐเชฟเชงเชฎ เช‰เชชเชฏเซ‹เช—เซ€ เชฅเชถเซ‡ เชœเซ‹ เชคเชฎเชพเชฐเซ‡ "เชตเซƒเช•เซเชท เช‰เชชเชฐ" เชคเชฎเชพเชฎ เช˜เชŸเช•เซ‹ เชฎเชพเชŸเซ‡ เชฐเซ‡เช•เซ‹เชฐเซเชกเซเชธ เชเช•เชคเซเชฐเชฟเชค เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชนเซ‹เชฏ, เชœเซเชฏเชพเชฐเซ‡ เชคเซ‡ เชฎเชพเชนเชฟเชคเซ€เชจเซ‡ เชœเชพเชณเชตเซ€ เชฐเชพเช–เชคเซ€ เชตเช–เชคเซ‡ (เช…เชจเซ‡ เช•เชฏเชพ เชธเซ‚เชšเช•เชพเช‚เช•เซ‹ เชธเชพเชฅเซ‡) เชคเซ‡เชจเซ‡ เชจเชฎเซ‚เชจเชพเชฎเชพเช‚ เชธเชฎเชพเชตเชฟเชทเซเชŸ เช•เชฐเชตเชพเชจเซเช‚ เช•เชพเชฐเชฃ เชฌเชจเซ‡ เช›เซ‡ - เช‰เชฆเชพเชนเชฐเชฃ เชคเชฐเซ€เช•เซ‡, เชธเชพเชฐเชพเช‚เชถ เช…เชนเซ‡เชตเชพเชฒ เชœเชจเชฐเซ‡เชŸ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชจเซ‹เชกเซเชธเชฎเชพเช‚ เชเช•เชคเซเชฐเซ€เช•เชฐเชฃ เชธเชพเชฅเซ‡.

PostgreSQL เชเชจเซเชŸเชฟเชชเซ‡เชŸเชฐเซเชจ: เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเชพ เชŠเช‚เชกเชพ เช›เซ‡? เชšเชพเชฒเซ‹ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชชเชธเชพเชฐ เช•เชฐเซ€เช
เชจเซ€เชšเซ‡เชจเซ€ เชฌเชพเชฌเชคเซ‹เชจเซ‡ เชฎเชพเชคเซเชฐ เช–เซเชฏเชพเชฒเชจเชพ เชชเซเชฐเชพเชตเชพ เชคเชฐเซ€เช•เซ‡ เชœ เชฒเซ‡เชตเซ€ เชœเซ‹เชˆเช, เช•เชพเชฐเชฃ เช•เซ‡ เชตเชฟเชจเช‚เชคเซ€ เช–เซ‚เชฌ เชœ เชฌเซ‹เชœเชพเชฐเซ‚เชช เช›เซ‡. เชชเชฐเช‚เชคเซ เชœเซ‹ เชคเซ‡ เชคเชฎเชพเชฐเชพ เชกเซ‡เชŸเชพเชฌเซ‡เช เชชเชฐ เชชเซเชฐเชญเซเชคเซเชต เชงเชฐเชพเชตเซ‡ เช›เซ‡, เชคเซ‹ เชคเชฎเชพเชฐเซ‡ เชธเชฎเชพเชจ เชคเช•เชจเซ€เช•เซ‹เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเชตเชพ เชตเชฟเชถเซ‡ เชตเชฟเชšเชพเชฐเชตเซเช‚ เชœเซ‹เชˆเช.

เชšเชพเชฒเซ‹ เช•เซ‡เชŸเชฒเชพเช• เชธเชฐเชณ เชจเชฟเชตเซ‡เชฆเชจเซ‹ เชธเชพเชฅเซ‡ เชชเซเชฐเชพเชฐเช‚เชญ เช•เชฐเซ€เช:

  • เชกเซ‡เชŸเชพเชฌเซ‡เชเชฎเชพเช‚เชฅเซ€ เชธเชฎเชพเชจ เชฐเซ‡เช•เซ‹เชฐเซเชก เชคเซ‡เชจเซ‡ เชซเช•เซเชค เชเช• เชœ เชตเชพเชฐ เชตเชพเช‚เชšเชตเซเช‚ เชถเซเชฐเซ‡เชทเซเช  เช›เซ‡.
  • เชกเซ‡เชŸเชพเชฌเซ‡เชเชฎเชพเช‚เชฅเซ€ เชฐเซ‡เช•เซ‹เชฐเซเชกเซเชธ เชฌเซ‡เชšเชฎเชพเช‚ เชตเชพเช‚เชšเชตเชพ เชฎเชพเชŸเซ‡ เชคเซ‡ เชตเชงเซ เช•เชพเชฐเซเชฏเช•เซเชทเชฎ เช›เซ‡เชเช•เชฒเชพ เช•เชฐเชคเชพเช‚.

เชšเชพเชฒเซ‹ เชนเชตเซ‡ เช†เชชเชฃเชจเซ‡ เชœเซ‹เชˆเชคเซ€ เชตเชฟเชจเช‚เชคเซ€ เชคเซˆเชฏเชพเชฐ เช•เชฐเชตเชพเชจเซ‹ เชชเซเชฐเชฏเชพเชธ เช•เชฐเซ€เช.

เชชเช—เชฒเซเช‚ 1

เชฆเซ‡เช–เซ€เชคเซ€ เชฐเซ€เชคเซ‡, เชœเซเชฏเชพเชฐเซ‡ เชชเซเชจเชฐเชพเชตเชฐเซเชคเชจ เชถเชฐเซ‚ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ (เชคเซ‡ เชตเชฟเชจเชพ เช†เชชเชฃเซ‡ เช•เซเชฏเชพเช‚ เชนเซ‹เชˆเชถเซเช‚!) เช†เชชเชฃเซ‡ เชชเซเชฐเชพเชฐเช‚เชญเชฟเช• เช“เชณเช–เช•เชฐเซเชคเชพเช“เชจเชพ เชธเชฎเซ‚เชนเชจเชพ เช†เชงเชพเชฐเซ‡ เชชเชพเช‚เชฆเชกเชพเชจเชพ เชฐเซ‡เช•เซ‹เชฐเซเชกเชจเซ‡ เชฌเชพเชฆเชฌเชพเช•เซ€ เช•เชฐเชตเซ€ เชชเชกเชถเซ‡:

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, เชชเชฐเช‚เชคเซ เชเชฐเซ‡ เชฎเชพเชŸเซ‡ เชจเชนเซ€เช‚. เชœเซ‹เช•เซ‡ เชคเซ‡เชฃเซ€ เชคเชฎเชพเชฐเชพ เชชเซ‹เชคเชพเชจเชพ เชชเชฐ เช…เชฎเชฒ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชธเชฐเชณ.

เชชเช—เชฒเซเช‚ 2

เชนเชตเซ‡ เช…เชฎเชจเซ‡ เชตเชฟเชญเชพเช— ID เชจเซ‹ เชธเชฎเซ‚เชน เชฎเชณเชถเซ‡ เชœเซ‡เชจเซ‡ เช†เช—เชณ เชตเชพเช‚เชšเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชชเชกเชถเซ‡. เชฒเช—เชญเช— เชนเช‚เชฎเซ‡เชถเชพ เชคเซ‡เช“ เชฎเซ‚เชณ เชธเซ‡เชŸเชจเชพ เชตเชฟเชตเชฟเชง เชฐเซ‡เช•เซ‹เชฐเซเชกเซเชธเชฎเชพเช‚ เชกเซเชชเซเชฒเชฟเช•เซ‡เชŸ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡ - เชคเซ‡เชฅเซ€ เช…เชฎเซ‡ เช•เชฐเซ€เชถเซเช‚ เชคเซ‡เชฎเชจเซ‡ เชœเซ‚เชฅ เช•เชฐเซ‹, เชธเซเชคเซเชฐเซ‹เชค เชชเชพเช‚เชฆเชกเชพ เชตเชฟเชถเซ‡ เชฎเชพเชนเชฟเชคเซ€ เชธเชพเชšเชตเชคเซ€ เชตเช–เชคเซ‡.

เชชเชฐเช‚เชคเซ เช…เชนเซ€เช‚ เชคเซเชฐเชฃ เชฎเซเชถเซเช•เซ‡เชฒเซ€เช“ เช†เชชเชฃเซ€ เชฐเชพเชน เชœเซ‹เชถเซ‡:

  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 เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เช…เชจเซเช•เชฐเชฃ เช•เชฐเชตเชพเชจเซ€ เชเช• เชธเชฐเชณ เชฐเซ€เชค เช›เซ‡ 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

เชนเชตเซ‡ เช†เชชเชฃเซ‡ เชœเซ‹เชˆเช เช›เซ€เช เช•เซ‡ เชถเชพ เชฎเชพเชŸเซ‡ เชธเช‚เช–เซเชฏเชพเชคเซเชฎเช• ID เชŸเซ‡เช•เซเชธเซเชŸเชฎเชพเช‚ เชซเซ‡เชฐเชตเชพเชˆ เชนเชคเซ€ - เชœเซ‡เชฅเซ€ เชคเซ‡เช“ เช…เชฒเซเชชเชตเชฟเชฐเชพเชฎเชฅเซ€ เช…เชฒเช— เชฅเชˆเชจเซ‡ เชเช•เชธเชพเชฅเซ‡ เชœเซ‹เชกเชพเชˆ เชถเช•เซ‡!

เชชเช—เชฒเซเช‚ 3

เชซเชพเชˆเชจเชฒ เชฎเชพเชŸเซ‡ เช…เชฎเชพเชฐเซ€ เชชเชพเชธเซ‡ เช•เช‚เชˆ เชฌเชพเช•เซ€ เชจเชฅเซ€:

  • เช…เชฎเซ‡ เชœเซ‚เชฅเชฌเชฆเซเชง ID เชจเชพ เชธเชฎเซ‚เชนเชจเชพ เช†เชงเชพเชฐเซ‡ "เชตเชฟเชญเชพเช—" เชฐเซ‡เช•เซ‹เชฐเซเชก เชตเชพเช‚เชšเซ€เช เช›เซ€เช
  • เช…เชฎเซ‡ เชฌเชพเชฆเชฌเชพเช•เซ€ เช•เชฐเซ‡เชฒ เชตเชฟเชญเชพเช—เซ‹เชจเซ€ เชคเซเชฒเชจเชพ เชฎเซ‚เชณ เชถเซ€เชŸเซเชธเชจเชพ "เชธเซ‡เชŸเซเชธ" เชธเชพเชฅเซ‡ เช•เชฐเซ€เช เช›เซ€เช
  • เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชธเซ‡เชŸ-เชธเซเชŸเซเชฐเชฟเช‚เช—เชจเซ‡ "เชตเชฟเชธเซเชคเซƒเชค เช•เชฐเซ‹". 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 เชเชจเซเชŸเชฟเชชเซ‡เชŸเชฐเซเชจ: เชธเชธเชฒเชพเชจเชพ เช›เชฟเชฆเซเชฐ เช•เซ‡เชŸเชฒเชพ เชŠเช‚เชกเชพ เช›เซ‡? เชšเชพเชฒเซ‹ เชตเช‚เชถเชตเซ‡เชฒเซ‹ เชชเชธเชพเชฐ เช•เชฐเซ€เช
[explan.tensor.ru เชชเชฐ เชœเซเช“]

เชธเซ‹เชฐเซเชธ: www.habr.com

เชเช• เชŸเชฟเชชเซเชชเชฃเซ€ เช‰เชฎเซ‡เชฐเซ‹