PostgreSQL Antipatterns: рдЦрд░рдЧреЛрд╢ рдЫреЗрдж рдХрд┐рддрдирд╛ рдЧрд╣рд░рд╛ рд╣реИ? рдкрджрд╛рдиреБрдХреНрд░рдо рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЬрд╛рдирд╛

рдЬрдЯрд┐рд▓ рдИрдЖрд░рдкреА-рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ, рдХрдИ рд╕рдВрд╕реНрдерд╛рдУрдВ рдореЗрдВ рдПрдХ рдкрджрд╛рдиреБрдХреНрд░рдорд┐рдд рдкреНрд░рдХреГрддрд┐ рд╣реЛрддреА рд╣реИ , рдЬрдм рд╕рдЬрд╛рддреАрдп рд╡рд╕реНрддреБрдПрдВ рдкреВрд░реНрд╡рдЬ-рд╡рдВрд╢рдЬ рд╕рдВрдмрдВрдз рдкреЗрдбрд╝ рдореЗрдВ рдкрдВрдХреНрддрд┐рдмрджреНрдз рд╣реЛрддреА рд╣реИрдВ - рдпрд╣ рдЙрджреНрдпрдо рдХреА рд╕рдВрдЧрдардирд╛рддреНрдордХ рд╕рдВрд░рдЪрдирд╛ (рдЗрди рд╕рднреА рд╢рд╛рдЦрд╛рдУрдВ, рд╡рд┐рднрд╛рдЧреЛрдВ рдФрд░ рдХрд╛рд░реНрдп рд╕рдореВрд╣реЛрдВ), рдФрд░ рдЙрддреНрдкрд╛рдж рд╕реВрдЪреА, рдФрд░ рдХрд╛рд░реНрдп рдХреНрд╖реЗрддреНрд░, рдФрд░ рднреВрдЧреЛрд▓ рд╣реИред рдмрд┐рдХреНрд░реА рдХреЗ рдмрд┐рдВрджреБ, ... рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рд╡реНрдпрд╛рдкрд╛рд░ рд╕реНрд╡рдЪрд╛рд▓рди рдХрд╛ рдПрдХ рднреА рдХреНрд╖реЗрддреНрд░ рдирд╣реАрдВ рд╣реИ рдЬрд╣рд╛рдВ рдХрдо рд╕реЗ рдХрдо рдХреБрдЫ рдкрджрд╛рдиреБрдХреНрд░рдо рдХрд╛ рдкрд░рд┐рдгрд╛рдо рдирд╣реАрдВ рд╣реЛрдЧрд╛ред рд▓реЗрдХрд┐рди рднрд▓реЗ рд╣реА рдЖрдк "рд╡реНрдпрд╛рдкрд╛рд░ рдХреЗ рд▓рд┐рдП" рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рдлрд┐рд░ рднреА рдЖрдк рдЖрд╕рд╛рдиреА рд╕реЗ рдкрджрд╛рдиреБрдХреНрд░рдорд┐рдд рд░рд┐рд╢реНрддреЛрдВ рдореЗрдВ рдЖ рд╕рдХрддреЗ рд╣реИрдВред рдЯреНрд░рд╛рдЗрдЯ, рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рд╢реЙрдкрд┐рдВрдЧ рд╕реЗрдВрдЯрд░ рдореЗрдВ рдЖрдкрдХреЗ рдкрд░рд┐рд╡рд╛рд░ рдХреЗ рдкреЗрдбрд╝ рдпрд╛ рдлрд░реНрд╢ рдХреА рдпреЛрдЬрдирд╛ рднреА рдПрдХ рд╣реА рд╕рдВрд░рдЪрдирд╛ рд╣реИред рдбреАрдмреАрдПрдордПрд╕ рдореЗрдВ рдЗрд╕ рддрд░рд╣ рдХреЗ рдкреЗрдбрд╝ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рдХрдИ рддрд░реАрдХреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЖрдЬ рд╣рдо рдХреЗрд╡рд▓ рдПрдХ рд╡рд┐рдХрд▓реНрдк рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд░реЗрдВрдЧреЗ:







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

CREATE INDEX ON hier(pid); --  ,  FK    ,    PK

рдФрд░ рдЬрдм рдЖрдк рдкрджрд╛рдиреБрдХреНрд░рдо рдХреА рдЧрд╣рд░рд╛рдИ рдореЗрдВ рдЭрд╛рдВрдХрддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рдзреИрд░реНрдпрдкреВрд░реНрд╡рдХ рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдЗрд╕ рддрд░рд╣ рдХреА рд╕рдВрд░рдЪрдирд╛ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рдЖрдкрдХреЗ "рднреЛрд▓реЗ" рддрд░реАрдХреЗ рдХреИрд╕реЗ рдирд┐рдХрд▓реЗрдЧреЗрдВред


рдЖрдЗрдП SQL рдореЗрдВ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдЙрднрд░рддреЗ рдХрд╛рд░реНрдпреЛрдВ, рдЙрдирдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░реЗрдВ рдФрд░ рдЙрдирдХреЗ рдкреНрд░рджрд░реНрд╢рди рдХреЛ рдмреЗрд╣рддрд░ рдмрдирд╛рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВред

# 1ред рдЦрд░рдЧреЛрд╢ рдЫреЗрдж рдХрд┐рддрдирд╛ рдЧрд╣рд░рд╛ рд╣реИ?


рдЖрдЗрдП, рдирд┐рд╢реНрдЪрд┐рддрддрд╛ рдХреЗ рд▓рд┐рдП, рдХрд┐ рдпрд╣ рд╕рдВрд░рдЪрдирд╛ рд╕рдВрдЧрдарди рдХреА рд╕рдВрд░рдЪрдирд╛ рдореЗрдВ рд╡рд┐рднрд╛рдЧреЛрдВ рдХреА рдЕрдзреАрдирддрд╛ рдХреЛ рджрд░реНрд╢рд╛рддреА рд╣реИ: рд╡рд┐рднрд╛рдЧ, рд╡рд┐рднрд╛рдЧ, рдХреНрд╖реЗрддреНрд░, рд╢рд╛рдЦрд╛рдПрдБ, рдХрд╛рд░реНрдп рд╕рдореВрд╣ ... рдЬреЛ рднреА рдЖрдк рдЙрдиреНрд╣реЗрдВ рдХрд╣рддреЗ рд╣реИрдВред

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдо 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 рд╕реЗ рдЕрдзрд┐рдХ рд╕реНрддрд░ рд╣реИрдВ, рдФрд░ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рджрд░реНрдЬрдиреЛрдВ рд╡рдВрд╢рдЬ рд╣реИрдВ, рддреЛ рд╕рдорд╕реНрдпрд╛рдПрдВ рд╣реЛ рд╕рдХрддреА рд╣реИрдВред рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рдкрд╛рд░рдВрдкрд░рд┐рдХ "рдкреЗрдбрд╝ рдХреЗ рдиреАрдЪреЗ" рдЦреЛрдЬ рд╡рд┐рдХрд▓реНрдк рдХреИрд╕реЗ рд▓рд┐рдЦреЗ рдЧрдП рд╣реИрдВ (рдФрд░ рдХрд╛рдо)ред рд▓реЗрдХрд┐рди рдкрд╣рд▓реЗ, рд╣рдо рдпрд╣ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рд╣рдорд╛рд░реЗ рд╢реЛрдз рдХреЗ рд▓рд┐рдП рдХреМрди рд╕рд╛ рдиреЛрдб рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рд╣реЛрдЧрд╛ред "рдЧрд╣рд░реЗ" subtrees:



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

"рд╡реНрдпрд╛рдкрдХ" subtrees:

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

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

рдЗрди рдкреНрд░рд╢реНрдиреЛрдВ рдХреЗ рд▓рд┐рдП, рд╣рдордиреЗ рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рдкреБрдирд░рд╛рд╡рд░реНрддреА рдЬреЙрдп рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ :


рдЬрд╛рд╣рд┐рд░ рд╣реИ, рдЗрд╕ рдХреНрд╡реЗрд░реА рдореЙрдбрд▓ рдХреЗ рд╕рд╛рде, рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреБрд▓ рд╡рдВрд╢рдЬреЛрдВ рдХреЗ рд╕рд╛рде рдореЗрд▓ рдЦрд╛рдПрдЧреА (рдФрд░ рдЙрдирдореЗрдВ рд╕реЗ рдХрдИ рджрд░реНрдЬрди рд╣реИрдВ), рдФрд░ рдпрд╣ рдХрд╛рдлреА рдкрд░реНрдпрд╛рдкреНрдд рд╕рдВрд╕рд╛рдзрди рд▓реЗ рд╕рдХрддрд╛ рд╣реИ, рдФрд░, рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк, рд╕рдордпред

рдЪрд▓реЛ "рд╡реНрдпрд╛рдкрдХ" рд╕рдмрдЯреНрд░реА рдкрд░ рдЬрд╛рдБрдЪ рдХрд░реЗрдВ:

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;


[рд╕реНрдкрд╖реНрдЯреАрдХрд░рдг рдкрд░ рджреЗрдЦреЗрдВ редensor.ru]

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

рд╕реВрдЪрдХрд╛рдВрдХ рджреНрд╡рд╛рд░рд╛ рдмрдбрд╝реЗ рдкреИрдорд╛рдиреЗ рдкрд░ рдкреНрд░реВрдлрд░реАрдбрд┐рдВрдЧ


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

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

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

рдпрд╣ рдереЛрдбрд╝рд╛ рдЬрдВрдЧрд▓реА рд▓рдЧрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЖрд░реЗрдЦ рдкрд░ - рд╕рдм рдХреБрдЫ рд╕рд░рд▓ рд╣реИред



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;


[рд╕реНрдкрд╖реНрдЯреАрдХрд░рдг рдкрд░ рджреЗрдЦреЗрдВ редensor.ru]

рдФрд░ рдпрд╣рд╛рдБ рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рд╕рдордп рдореЗрдВ 1.5 рдмрд╛рд░ рднреА рдЬреАрдд рдирд╣реАрдВ рд░рд╣реА рд╣реИ , рд▓реЗрдХрд┐рди рдпрд╣ рдХрд┐ рд╣рдордиреЗ рдХрдо рдмрдлрд╝рд░реНрд╕ рдХреЛ рдШрдЯрд╛рдпрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ 30 рдХреЗ рдмрдЬрд╛рдп рд╕реВрдЪрдХрд╛рдВрдХ рдореЗрдВ рдХреЗрд╡рд▓ 5 рдХреЙрд▓ рд╣реИрдВ!

рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд рдмреЛрдирд╕ рддрдереНрдп рдпрд╣ рд╣реИ рдХрд┐ рдЕрдВрддрд┐рдо рдЕрдирд╛рд╡рд╢реНрдпрдХ рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛рдУрдВ рдХреЗ рдмрд╛рдж "рд╕реНрддрд░" рджреНрд╡рд╛рд░рд╛ рдЖрджреЗрд╢ рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рдиреЛрдб рдЯреИрдЧ


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

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



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;


[Expl.t.toror.ru рдкрд░ рджреЗрдЦреЗрдВ]

рд╣рдо рдЗрдВрдбреЗрдХреНрд╕ рдореЗрдВ рдПрдХ рдФрд░ рдЕрдкреАрд▓ рдХреЛ рдХрдо рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдереЗ рдФрд░ рдХрдЯреМрддреА рдХреА рдЧрдИ рд░рд╛рд╢рд┐ рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ 2 рдЧреБрдирд╛ рд╕реЗ рдЕрдзрд┐рдХ рдЬреАрддреЗ ред

# реи рд╡рд╛рдкрд╕ рдЬрдбрд╝реЛрдВ рдХреА рдУрд░


рдпрд╣ рдПрд▓реНрдЧреЛрд░рд┐рдердо рддрдм рдЙрдкрдпреЛрдЧреА рд╣реЛрдЧрд╛ рдЬрдм рдЖрдкрдХреЛ рд╕рднреА рд╕реНрд░реЛрдд "рдкреЗрдбрд╝" рдХреЗ рд▓рд┐рдП рд░рд┐рдХреЙрд░реНрдб рдЗрдХрдЯреНрдард╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдЬрдмрдХрд┐ рд╕реНрд░реЛрдд рд╢реАрдЯ (рдФрд░ рдХрд┐рд╕ рд╕рдВрдХреЗрддрдХ рдХреЗ рд╕рд╛рде) рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рдмрдирд╛рдП рд░рдЦрдиреЗ рдХреЗ рдХрд╛рд░рдг рдпрд╣ рдирдореВрдирд╛ рдореЗрдВ рдЧрд┐рд░ рдЬрд╛рддрд╛ рд╣реИ - рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдиреЛрдбреНрд╕ рдХреЗ рдПрдХрддреНрд░реАрдХрд░рдг рдХреЗ рд╕рд╛рде рдПрдХ рд╕рд╛рд░рд╛рдВрд╢ рд░рд┐рдкреЛрд░реНрдЯ рддреИрдпрд╛рд░ рдХрд░рдирд╛ред


рдЕрдиреБрд░реЛрдз рдХреЛ рдмрд╣реБрдд рд╣реА рдкреНрд░рдорд╛рдг рдХреЗ рд░реВрдк рдореЗрдВ рд▓рд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рдХреНрдпреЛрдВрдХрд┐ рдЕрдиреБрд░реЛрдз рдмрд╣реБрдд рдмреЛрдЭрд┐рд▓ рд╣реИред рд▓реЗрдХрд┐рди рдЕрдЧрд░ рдпрд╣ рдЖрдкрдХреЗ рдбреЗрдЯрд╛рдмреЗрд╕ рдкрд░ рд╣рд╛рд╡реА рд╣реИ - рдпрд╣ рдРрд╕реА рддрдХрдиреАрдХреЛрдВ рдХреЗ рдЙрдкрдпреЛрдЧ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░рдиреЗ рдпреЛрдЧреНрдп рд╣реИред

рдЖрдЗрдП рдХреБрдЫ рд╕рд░рд▓ рдХрдердиреЛрдВ рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ:

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

рдЕрдм рд╣рдо рдЙрд╕ рдХреНрд╡реЗрд░реА рдХреЛ рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рддреЗ рд╣реИрдВ рдЬрд┐рд╕рдХреА рд╣рдореЗрдВ рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред

рдЪрд░рдг 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. рдХреНрд╡реЗрд░реА рдХреЗ "рдЙрдк-рдкреБрдирд░рд╛рд╡рд░реНрддреА" рднрд╛рдЧ рдореЗрдВ рдХреБрд▓ рдХрд╛рд░реНрдп рдирд╣реАрдВ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ c GROUP BYред
  2. рдПрдХ рдкреБрдирд░рд╛рд╡рд░реНрддреА "рддрд╛рд▓рд┐рдХрд╛" рдХреЗ рд▓рд┐рдП рдХреЙрд▓ рдПрдХ рдиреЗрд╕реНрдЯреЗрдб рдЙрдкрд╢реНрд░реЗрдгреА рдореЗрдВ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред
  3. рдкреБрдирд░рд╛рд╡рд░реНрддреА рднрд╛рдЧ рдореЗрдВ рдПрдХ рдХреНрд╡реЗрд░реА рдореЗрдВ CTE рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред

рд╕реМрднрд╛рдЧреНрдп рд╕реЗ, рдЗрди рд╕рднреА рд╕рдорд╕реНрдпрд╛рдУрдВ рдХреЛ рдХрд╛рдлреА рдЖрд╕рд╛рдиреА рд╕реЗ рдмрд╛рдИрдкрд╛рд╕ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЪрд▓рд┐рдП рдЕрдВрдд рд╕реЗ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред

рдкреБрдирд░рд╛рд╡рд░реНрддреА рднрд╛рдЧ рдореЗрдВ рд╕реАрдЯреАрдИ


рдпрд╣ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ:

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

рдФрд░ рдЗрд╕рд▓рд┐рдП рдпрд╣ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдХреЛрд╖реНрдардХ рддрдп рдХрд░рддреЗ рд╣реИрдВ!

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

рдкреБрдирд░рд╛рд╡рд░реНрддреА "рддрд╛рд▓рд┐рдХрд╛" рдХреЗ рд▓рд┐рдП рдиреЗрд╕реНрдЯреЗрдб рдХреНрд╡реЗрд░реА


рд╣рдореНрдо ... рдПрдХ рдкреБрдирд░рд╛рд╡рд░реНрддреА CTE рдХреЗ рд▓рд┐рдП рдПрдХ рдХреЙрд▓ рдПрдХ рдЙрдкрд╢реНрд░реЗрдгреА рдореЗрдВ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ред рд▓реЗрдХрд┐рди рдпрд╣ рд╕реАрдЯреАрдИ рдХреЗ рдЕрдВрджрд░ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ! рдПрдХ рдЙрдк-рдЕрдиреБрд░реЛрдз рдкрд╣рд▓реЗ рд╣реА рдЗрд╕ CTE рдХреЛ рд╕рдВрджрд░реНрднрд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реИ!

рдкреБрдирд░рд╛рд╡рд░реНрддреА рдХреЗ рдЕрдВрджрд░ рдЧреНрд░реБрдк рдмрд╛рдп


рдпрд╣ рдЕрдкреНрд░рд┐рдп рд╣реИ, рд▓реЗрдХрд┐рди ... рд╣рдорд╛рд░реЗ рдкрд╛рд╕ 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;


[ explain.tensor.ru]

All Articles