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 рдПрдВрдЯреАрдкреИрдЯрд░реНрди: рдЦрд░рдЧреЛрд╢ рдХрд╛ рдЫреЗрдж рдХрд┐рддрдирд╛ рдЧрд╣рд░рд╛ рд╣реИ? рдЖрдЗрдП рдкрджрд╛рдиреБрдХреНрд░рдо рд╕реЗ рдЧреБрдЬрд░реЗрдВ

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдЖрдЗрдП 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;

рдЖрдЗрдП рд╕рдмрд╕реЗ рд╕рд░рд▓ рдХрд╛рд░реНрдп рд╕реЗ рд╢реБрд░реВ рдХрд░реЗрдВ - рдЙрди рд╕рднреА рдХрд░реНрдордЪрд╛рд░рд┐рдпреЛрдВ рдХреЛ рдвреВрдВрдврдирд╛ рдЬреЛ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреНрд╖реЗрддреНрд░ рдореЗрдВ рдпрд╛ рдкрджрд╛рдиреБрдХреНрд░рдо рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ - рдПрдХ рдиреЛрдб рдХреЗ рд╕рднреА рдмрдЪреНрдЪреЛрдВ рдХреЛ рдЦреЛрдЬреЗрдВ. рд╡рдВрд╢рдЬ рдХреА "рдЧрд╣рд░рд╛рдИ" рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рднреА рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛... рдпрд╣ рд╕рдм рдЖрд╡рд╢реНрдпрдХ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдХрд┐рд╕реА рдкреНрд░рдХрд╛рд░ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрди рдХрд░реНрдордЪрд╛рд░рд┐рдпреЛрдВ рдХреА рдЖрдИрдбреА рдХреА рд╕реВрдЪреА рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЬрдЯрд┐рд▓ рдЪрдпрди.

рдпрджрд┐ рдЗрди рд╡рдВрд╢рдЬреЛрдВ рдХреЗ рдХреЗрд╡рд▓ рдХреБрдЫ рд╕реНрддрд░ рд╣реЛрдВ рдФрд░ рд╕рдВрдЦреНрдпрд╛ рдПрдХ рджрд░реНрдЬрди рдХреЗ рднреАрддрд░ рд╣реЛ рддреЛ рд╕рдм рдХреБрдЫ рдареАрдХ рд╣реЛрдЧрд╛, рд▓реЗрдХрд┐рди рдпрджрд┐ 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 рдПрдВрдЯреАрдкреИрдЯрд░реНрди: рдЦрд░рдЧреЛрд╢ рдХрд╛ рдЫреЗрдж рдХрд┐рддрдирд╛ рдЧрд╣рд░рд╛ рд╣реИ? рдЖрдЗрдП рдкрджрд╛рдиреБрдХреНрд░рдо рд╕реЗ рдЧреБрдЬрд░реЗрдВ
[рджреЗрдЦреЗрдВ рд╡реНрдпрд╛рдЦреНрдпрд╛.рдЯреЗрдВрд╕рд░.рдЖрд░рдпреВ]

рдЬреИрд╕реА рдХрд┐ рдЙрдореНрдореАрдж рдереА, рд╣рдореЗрдВ рд╕рднреА 30 рд░рд┐рдХреЙрд░реНрдб рдорд┐рд▓ рдЧрдпреЗред рд▓реЗрдХрд┐рди рдЙрдиреНрд╣реЛрдВрдиреЗ рдХреБрд▓ рд╕рдордп рдХрд╛ 60% рдЗрд╕реА рдкрд░ рдЦрд░реНрдЪ рдХрд┐рдпрд╛ - рдХреНрдпреЛрдВрдХрд┐ рдЙрдиреНрд╣реЛрдВрдиреЗ рдЗрдВрдбреЗрдХреНрд╕ рдореЗрдВ 30 рдЦреЛрдЬреЗрдВ рднреА рдХреАрдВред рдХреНрдпрд╛ рдЗрд╕рд╕реЗ рдХрдо рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реИ?

рд╕реВрдЪрдХрд╛рдВрдХ рджреНрд╡рд╛рд░рд╛ рдереЛрдХ рдкреНрд░реВрдлрд░реАрдбрд┐рдВрдЧ

рдХреНрдпрд╛ рд╣рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рдиреЛрдб рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрд▓рдЧ рд╕реВрдЪрдХрд╛рдВрдХ рдХреНрд╡реЗрд░реА рдмрдирд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ? рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ рдирд╣реАрдВ - рд╣рдо рд╕реВрдЪрдХрд╛рдВрдХ рд╕реЗ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ рдПрдХ рдХреЙрд▓ рдореЗрдВ рдПрдХ рд╕рд╛рде рдХрдИ рдХреБрдВрдЬрд┐рдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ = ANY(array).

рдФрд░ рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛рдУрдВ рдХреЗ рдРрд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рд╕рдореВрд╣ рдореЗрдВ рд╣рдо рдкрд┐рдЫрд▓реЗ рдЪрд░рдг рдореЗрдВ рдорд┐рд▓реА рд╕рднреА рдЖрдИрдбреА рдХреЛ "рдиреЛрдбреНрд╕" рджреНрд╡рд╛рд░рд╛ рд▓реЗ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╛рдиреА рд╣рд░ рдЕрдЧрд▓реЗ рдХрджрдо рдкрд░ рд╣рдо рдХрд░реЗрдВрдЧреЗ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рд╕реНрддрд░ рдХреЗ рд╕рднреА рд╡рдВрд╢рдЬреЛрдВ рдХреЛ рдПрдХ рд╕рд╛рде рдЦреЛрдЬреЗрдВ.

рдмрд╕, рдпрд╣реАрдВ рд╕рдорд╕реНрдпрд╛ рд╣реИ, рдкреБрдирд░рд╛рд╡рд░реНрддреА рдЪрдпрди рдореЗрдВ, рдЖрдк рдиреЗрд╕реНрдЯреЗрдб рдХреНрд╡реЗрд░реА рдореЗрдВ рд╕реНрд╡рдпрдВ рддрдХ рдирд╣реАрдВ рдкрд╣реБрдВрдЪ рд╕рдХрддреЗ, рд▓реЗрдХрд┐рди рд╣рдореЗрдВ рдХрд┐рд╕реА рддрд░рд╣ рдХреЗрд╡рд▓ рд╡рд╣реА рдЪреБрдирдиреЗ рдХреА рдЬрд╝рд░реВрд░рдд рд╣реИ рдЬреЛ рдкрд┐рдЫрд▓реЗ рд╕реНрддрд░ рдкрд░ рдкрд╛рдпрд╛ рдЧрдпрд╛ рдерд╛... рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рд╣реИ рдХрд┐ рд╕рдВрдкреВрд░реНрдг рдЪрдпрди рдХреЗ рд▓рд┐рдП рдиреЗрд╕реНрдЯреЗрдб рдХреНрд╡реЗрд░реА рдмрдирд╛рдирд╛ рдЕрд╕рдВрднрд╡ рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕рдХреЗ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреНрд╖реЗрддреНрд░ рдХреЗ рд▓рд┐рдП рдпрд╣ рд╕рдВрднрд╡ рд╣реИред рдФрд░ рдпрд╣ рдлрд╝реАрд▓реНрдб рдПрдХ рд╕рд░рдгреА рднреА рд╣реЛ рд╕рдХрддреА рд╣реИ - рдЬрд┐рд╕рдХрд╛ рд╣рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ 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 рдПрдВрдЯреАрдкреИрдЯрд░реНрди: рдЦрд░рдЧреЛрд╢ рдХрд╛ рдЫреЗрдж рдХрд┐рддрдирд╛ рдЧрд╣рд░рд╛ рд╣реИ? рдЖрдЗрдП рдкрджрд╛рдиреБрдХреНрд░рдо рд╕реЗ рдЧреБрдЬрд░реЗрдВ
[рджреЗрдЦреЗрдВ рд╡реНрдпрд╛рдЦреНрдпрд╛.рдЯреЗрдВрд╕рд░.рдЖрд░рдпреВ]

рдФрд░ рдпрд╣рд╛рдВ рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд рдпрд╣ рднреА рдирд╣реАрдВ рд╣реИ рд╕рдордп рдореЗрдВ 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 рдПрдВрдЯреАрдкреИрдЯрд░реНрди: рдЦрд░рдЧреЛрд╢ рдХрд╛ рдЫреЗрдж рдХрд┐рддрдирд╛ рдЧрд╣рд░рд╛ рд╣реИ? рдЖрдЗрдП рдкрджрд╛рдиреБрдХреНрд░рдо рд╕реЗ рдЧреБрдЬрд░реЗрдВ
[рджреЗрдЦреЗрдВ рд╡реНрдпрд╛рдЦреНрдпрд╛.рдЯреЗрдВрд╕рд░.рдЖрд░рдпреВ]

рд╣рдо рдПрдХ рдФрд░ рдЗрдВрдбреЗрдХреНрд╕ рдХреЙрд▓ рдХреЛ рдХрдо рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдереЗ рд╡реЙрд▓реНрдпреВрдо рдореЗрдВ 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 рдХрджрдо

рдЕрдм рд╣рдореЗрдВ рдЕрдиреБрднрд╛рдЧ рдЖрдИрдбреА рдХрд╛ рдПрдХ рд╕реЗрдЯ рдорд┐рд▓реЗрдЧрд╛ рдЬрд┐рд╕реЗ рдЖрдЧреЗ рдкрдврд╝рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред рд▓рдЧрднрдЧ рд╣рдореЗрд╢рд╛ рдЙрдиреНрд╣реЗрдВ рдореВрд▓ рд╕реЗрдЯ рдХреЗ рд╡рд┐рднрд┐рдиреНрди рд░рд┐рдХреЙрд░реНрдб рдореЗрдВ рдбреБрдкреНрд▓рд┐рдХреЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ - рдЗрд╕рд▓рд┐рдП рд╣рдо рдХрд░реЗрдВрдЧреЗ рдЙрдиреНрд╣реЗрдВ рд╕рдореВрд╣рд┐рдд рдХрд░реЗрдВ, рд╕реНрд░реЛрдд рдкрддреНрддреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рд╕рдВрд░рдХреНрд╖рд┐рдд рдХрд░рддреЗ рд╣реБрдПред

рд▓реЗрдХрд┐рди рдпрд╣рд╛рдВ рддреАрди рдкрд░реЗрд╢рд╛рдирд┐рдпрд╛рдВ рд╣рдорд╛рд░рд╛ рдЗрдВрддрдЬрд╛рд░ рдХрд░ рд░рд╣реА рд╣реИрдВ:

  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

рдЕрдм рд╣рдо рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рд╕рдВрдЦреНрдпрд╛рддреНрдордХ рдЖрдИрдбреА рдХреЛ рдЯреЗрдХреНрд╕реНрдЯ рдореЗрдВ рдХреНрдпреЛрдВ рдмрджрд▓ рджрд┐рдпрд╛ рдЧрдпрд╛ - рддрд╛рдХрд┐ рдЙрдиреНрд╣реЗрдВ рдЕрд▓реНрдкрд╡рд┐рд░рд╛рдо рд╕реЗ рдЕрд▓рдЧ рдХрд░рдХреЗ рдПрдХ рд╕рд╛рде рдЬреЛрдбрд╝рд╛ рдЬрд╛ рд╕рдХреЗ!

3 рдХрджрдо

рдлрд╛рдЗрдирд▓ рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХреБрдЫ рднреА рдирд╣реАрдВ рдмрдЪрд╛ рд╣реИ:

  • рд╣рдо рд╕рдореВрд╣реАрдХреГрдд рдЖрдИрдбреА рдХреЗ рдПрдХ рд╕реЗрдЯ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ "рдЕрдиреБрднрд╛рдЧ" рд░рд┐рдХреЙрд░реНрдб рдкрдврд╝рддреЗ рд╣реИрдВ
  • рд╣рдо рдШрдЯрд╛рдП рдЧрдП рдЕрдиреБрднрд╛рдЧреЛрдВ рдХреА рддреБрд▓рдирд╛ рдореВрд▓ рд╢реАрдЯ рдХреЗ "рд╕реЗрдЯ" рд╕реЗ рдХрд░рддреЗ рд╣реИрдВ
  • рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕реЗрдЯ-рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХрд╛ "рд╡рд┐рд╕реНрддрд╛рд░" рдХрд░реЗрдВ 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 рдПрдВрдЯреАрдкреИрдЯрд░реНрди: рдЦрд░рдЧреЛрд╢ рдХрд╛ рдЫреЗрдж рдХрд┐рддрдирд╛ рдЧрд╣рд░рд╛ рд╣реИ? рдЖрдЗрдП рдкрджрд╛рдиреБрдХреНрд░рдо рд╕реЗ рдЧреБрдЬрд░реЗрдВ
[рджреЗрдЦреЗрдВ рд╡реНрдпрд╛рдЦреНрдпрд╛.рдЯреЗрдВрд╕рд░.рдЖрд░рдпреВ]

рд╕реНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╝реЗрдВ