рдЬрдЯрд┐рд▓ рдИрдЖрд░рдкреА рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рдХрдИ рд╕рдВрд╕реНрдерд╛рдУрдВ рдХреА рдкреНрд░рдХреГрддрд┐ рдкрджрд╛рдиреБрдХреНрд░рдорд┐рдд рд╣реЛрддреА рд╣реИрдЬрдм рд╕рдЬрд╛рддреАрдп рд╡рд╕реНрддреБрдПрдБ рдкрдВрдХреНрддрд┐рдмрджреНрдз рд╣реЛ рдЬрд╛рддреА рд╣реИрдВ рдкреВрд░реНрд╡рдЬ-рд╡рдВрд╢рдЬ рд╕рдВрдмрдВрдзреЛрдВ рдХрд╛ рд╡реГрдХреНрд╖ - рдпрд╣ рдЙрджреНрдпрдо рдХреА рд╕рдВрдЧрдардирд╛рддреНрдордХ рд╕рдВрд░рдЪрдирд╛ рд╣реИ (рдпреЗ рд╕рднреА рд╢рд╛рдЦрд╛рдПрдБ, рд╡рд┐рднрд╛рдЧ рдФрд░ рдХрд╛рд░реНрдп рд╕рдореВрд╣), рдФрд░ рдорд╛рд▓ рдХреА рд╕реВрдЪреА, рдФрд░ рдХрд╛рд░реНрдп рдХреЗ рдХреНрд╖реЗрддреНрд░, рдФрд░ рдмрд┐рдХреНрд░реА рдмрд┐рдВрджреБрдУрдВ рдХрд╛ рднреВрдЧреЛрд▓,...
рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рд╡рд╣рд╛рдБ рдХреЛрдИ рдирд╣реАрдВ рд╣реИ
рдРрд╕реЗ рдЯреНрд░реА рдХреЛ DBMS рдореЗрдВ рд╕реНрдЯреЛрд░ рдХрд░рдиреЗ рдХреЗ рдХрдИ рддрд░реАрдХреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЖрдЬ рд╣рдо рдХреЗрд╡рд▓ рдПрдХ рд╡рд┐рдХрд▓реНрдк рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд░реЗрдВрдЧреЗ:
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 рд╕реЗ рдЕрдзрд┐рдХ рд╕реНрддрд░ рд╣реИрдВ рдФрд░ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рджрд░реНрдЬрдиреЛрдВ рд╡рдВрд╢рдЬ рд╣реИрдВ, рддреЛ рд╕рдорд╕реНрдпрд╛рдПрдБ рд╣реЛ рд╕рдХрддреА рд╣реИрдВред рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рдкрд╛рд░рдВрдкрд░рд┐рдХ рдбрд╛рдЙрди-рдж-рдЯреНрд░реА рдЦреЛрдЬ рд╡рд┐рдХрд▓реНрдк рдХреИрд╕реЗ рд▓рд┐рдЦреЗ рдЬрд╛рддреЗ рд╣реИрдВ (рдФрд░ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ)ред рд▓реЗрдХрд┐рди рдкрд╣рд▓реЗ, рдЖрдЗрдП рдпрд╣ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░реЗрдВ рдХрд┐ рд╣рдорд╛рд░реЗ рд╢реЛрдз рдХреЗ рд▓рд┐рдП рдХреМрди рд╕реЗ рдиреЛрдб рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рд╣реЛрдВрдЧреЗред
рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ "рдЧрд╣рд░рд╛" рдЙрдкрд╡реГрдХреНрд╖:
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
рдЗрди рдкреНрд░рд╢реНрдиреЛрдВ рдХреЗ рд▓рд┐рдП рд╣рдордиреЗ рдареЗрда рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдкреБрдирд░рд╛рд╡рд░реНрддреА рд╢рд╛рдорд┐рд▓ рд╣реЛрдВ:
рдЬрд╛рд╣рд┐рд░ рд╣реИ, рдЗрд╕ рдЕрдиреБрд░реЛрдз рдореЙрдбрд▓ рдХреЗ рд╕рд╛рде рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рд╡рдВрд╢рдЬреЛрдВ рдХреА рдХреБрд▓ рд╕рдВрдЦреНрдпрд╛ рд╕реЗ рдореЗрд▓ рдЦрд╛рдПрдЧреА (рдФрд░ рдЙрдирдореЗрдВ рд╕реЗ рдХрдИ рджрд░реНрдЬрди рд╣реИрдВ), рдФрд░ рдЗрд╕рдореЗрдВ рдХрд╛рдлреА рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╕рдВрд╕рд╛рдзрди рд▓рдЧ рд╕рдХрддреЗ рд╣реИрдВ, рдФрд░, рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк, рд╕рдордпред
рдЖрдЗрдП "рд╕рдмрд╕реЗ рдЪреМрдбрд╝реЗ" рдЙрдкрд╡реГрдХреНрд╖ рдкрд░ рдЬрд╛рдБрдЪ рдХрд░реЗрдВ:
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;
рдЬреИрд╕реА рдХрд┐ рдЙрдореНрдореАрдж рдереА, рд╣рдореЗрдВ рд╕рднреА 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;
рдФрд░ рдпрд╣рд╛рдВ рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд рдпрд╣ рднреА рдирд╣реАрдВ рд╣реИ рд╕рдордп рдореЗрдВ 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
, рдЬреЛ рд╣рдореЗрдВ рдкреБрдирд░рд╛рд╡рд░реНрддреА "рддрд╛рд▓рд┐рдХрд╛" рдХреЗ рдлрд╝реАрд▓реНрдб рддрдХ рддреБрд░рдВрдд рдкрд╣реБрдВрдЪрдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрдЧрд╛, рдФрд░ рдХреБрдВрдЬреА рдХреЗ рд╕реЗрдЯ рдХреЛ рдХрдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдиреЛрдб рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдлрд╝рд┐рд▓реНрдЯрд░рд┐рдВрдЧ рд╕реНрдерд┐рддрд┐ рдХреЗ рд╕рд╛рде рдПрдХ рд╕рдордЧреНрд░ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдЧрд╛:
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;
рд╣рдо рдПрдХ рдФрд░ рдЗрдВрдбреЗрдХреНрд╕ рдХреЙрд▓ рдХреЛ рдХрдо рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдереЗ рд╡реЙрд▓реНрдпреВрдо рдореЗрдВ 2 рдЧреБрдирд╛ рд╕реЗ рдЕрдзрд┐рдХ рдЬреАрддрд╛ рдкреНрд░реВрдлрд╝рд░реАрдбред
#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 рдХрджрдо
рдЕрдм рд╣рдореЗрдВ рдЕрдиреБрднрд╛рдЧ рдЖрдИрдбреА рдХрд╛ рдПрдХ рд╕реЗрдЯ рдорд┐рд▓реЗрдЧрд╛ рдЬрд┐рд╕реЗ рдЖрдЧреЗ рдкрдврд╝рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред рд▓рдЧрднрдЧ рд╣рдореЗрд╢рд╛ рдЙрдиреНрд╣реЗрдВ рдореВрд▓ рд╕реЗрдЯ рдХреЗ рд╡рд┐рднрд┐рдиреНрди рд░рд┐рдХреЙрд░реНрдб рдореЗрдВ рдбреБрдкреНрд▓рд┐рдХреЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ - рдЗрд╕рд▓рд┐рдП рд╣рдо рдХрд░реЗрдВрдЧреЗ рдЙрдиреНрд╣реЗрдВ рд╕рдореВрд╣рд┐рдд рдХрд░реЗрдВ, рд╕реНрд░реЛрдд рдкрддреНрддреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рд╕рдВрд░рдХреНрд╖рд┐рдд рдХрд░рддреЗ рд╣реБрдПред
рд▓реЗрдХрд┐рди рдпрд╣рд╛рдВ рддреАрди рдкрд░реЗрд╢рд╛рдирд┐рдпрд╛рдВ рд╣рдорд╛рд░рд╛ рдЗрдВрддрдЬрд╛рд░ рдХрд░ рд░рд╣реА рд╣реИрдВ:
- рдХреНрд╡реЗрд░реА рдХреЗ "рд╕рдмрд░рд┐рдХрд░реНрд╕рд┐рд╡" рднрд╛рдЧ рдореЗрдВ рд╕рдордЧреНрд░ рдлрд╝рдВрдХреНрд╢рди рд╢рд╛рдорд┐рд▓ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддреЗ
GROUP BY
. - рдкреБрдирд░рд╛рд╡рд░реНрддреА "рддрд╛рд▓рд┐рдХрд╛" рдХрд╛ рд╕рдВрджрд░реНрдн рдиреЗрд╕реНрдЯреЗрдб рд╕рдмрдХреНрд╡реЗрд░реА рдореЗрдВ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ред
- рдкреБрдирд░рд╛рд╡рд░реНрддреА рднрд╛рдЧ рдореЗрдВ рдХрд┐рд╕реА рдЕрдиреБрд░реЛрдз рдореЗрдВ 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;
рд╕реНрд░реЛрдд: www.habr.com