αα αααα»ααααααααα ERP αααα»αααααΆα α’αααααΆαααΆα αααΎαααΆαααααααααΆααΆαα»αααααα ααααααααααα»ααΌα ααααΆαααααααα½α αααααΆαααααααΆααααααααΌαααΆ - ααΌαα α - αααααΊααΆαα ααΆααααααααα’αααααΆααααααα ααααΆα (ααΆααΆ ααΆααααααΆα αα·ααααα»αααΆαααΆαααΆααα’ααααα) αα·αααΆααΆα‘α»ααααα·α αα·ααααααααΆαααΆα αα·αααΌαα·ααΆαααααααα ααα»α ααα...
ααΆαααΆααα·ααα·αααΆαααα
ααΆααα·ααΈααΆα αααΎααααα»αααΆααααααΆαα»αααΎαααΎαααααααα αααα»α 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;
α
αΌαα
αΆααααααΎαααΆαα½αααΉααα·α
αα
ααΆαααααΆαααααααα»α - ααΆαααααααααα»ααααα·αααΆααα’αααααααααΎααΆααα
αααα»ααα·αααααΆααααΆαααα½α α¬αα
αααα»αααααααααααααΆααΆαα»αααα - αααααααααΌαααΆααα’αααα node. ααΆααΆααΆαααα’ααααααααα»αααΆαααα½αααΆα "ααααα
" ααααΌαα
α
... ααΆααα’αααααα’αΆα
ααΆααΆαα
αΆαααΆα
α α§ααΆα ααα ααΎααααΈαααΆααααααααα½αα
ααα½αα
α’αααΈαααΉαααα’ααααα·αααΎααΆαααΌαα α ααΆαααααααααΈαααΈααααα·α α αΎαα ααα½αααΆααααα»ααααααααα ααα»ααααααααα·αααΎααΆαααΎαααΈ 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!
ααααΆαααααααΆααααααααααΊααΆααΆααα·ααααααΆαααααΆααααΈααΆα unnest α α»αααααα ααΆααααααα’ααααααααΆαααΉααα αααααααΆααα "ααααα·α" α
αααααΆααααΆαα
ααΆααα·α αΆαααΆαααααΆαααααααΉααα½ααααααα’ααΆαα’αα»ααααααΊ β "ααααΉα" αα·αα’αΆα ααΆαααΌαααΆαααααααααΊαααααΆαααα½ααααα·αα αΆαααΆα αααΎα "α α»αααααα" ααΆαααααααα αα αααα»αααΆααααααΎαααΆααα·α αα ααααααΎα αααααΆααααααΆ ααααα·αααΎααΎαααααΎααΆααααααααααΆααααααΆααααααΆα α αΎαααΆαααααα»ααααα·α ααααα·αα αΆαααΆα αααααΎααααααααααααΆαααΆααΆαααααα
αααα
αΌααα
αααα»αααΆααΆαααααααΎαα αααααα 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 αααααα»αααα·ααΆα α’αΆαα‘αΎααα·αα
#α’. α αΌαααΎααααααααα α«α
αααα½ααααααααΆααααααΉαααΆαααααααααααααα·αααΎα’αααααααΌαααΆααααααΌααααααααααΆαααααΆααααΆαα»ααΆααα’αα "α‘αΎαααΎααΎαααΎ" ααααααααααααααΆααααααΆαα’αααΈαααααΉααααααααΆαα½α (αα·αααΆαα½αααΌα ααΆααα’αααΈαααα) ααααααααΆαα±ααααΆααααΌαααΆααααα αΌααααα»αααααΌ - α§ααΆα ααα ααΎααααΈαααααΎααααΆαααΆααααααααα ααΆαα½αααΉαααΆααααααΌααααα»ααα ααΆααααΆααα
α’αααΈβαααβαααααΆααβααβαα½αβααβααααΌαβααΆαβααβαα
βααΆβααααα»ααΆαβααβαααα·αβααβαα½αβααα α
αΆααβααΆααβααΈβααααΎβαααβααααΆαβββαα
βααΆβααΆαβααααΆαβααααΆααβααΆααβα ααα»ααααααααα·αααΎααΆααααααααααααΎααΌαααααΆααα·ααααααααααα’ααα α’ααααα½ααααα·αα’αααΈααΆαααααΎααααΆαααα
αα
αααααααααααααααΆα
α αΌαα αΆααααααΎαααΆαα½αααα ααααΈαααααααΆαααααΆαααααα½αα ααα½αα
- αααααααααΆααΌα ααααΆααΈααΌαααααΆααα·αααααα ααΆααΆααΆαααα’αααα»ααααα»αααΆαα’αΆαααΆαααααα.
- αααααααααΆααΈααΌαααααΆααα·αααααα ααΆααΆααααααΆαααααα·αααααΆααααα»αααΆαα’αΆαααΆααΆα αααΆααα ααααΆααα―αα
α₯α‘αΌαααα α αΌαααΎαααααΆααΆααααααΎαααααΎαααααΎαααααΌαααΆαα
αααααα αΆα 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
α₯α‘αΌααααααΎαααΉαααα½αααΆααααα»αααααααααααΆααααααααααααΉαααααΌαα’αΆαααααααα ααααΎαααααΆαα·α αα ααΆα αα½αααΆααΉαααααΌαααΆαα αααααα αααα»ααααααααααΆααααααααααΆαααααα»αααΎα - ααΌα ααααααΎαααΉα αααα»ααα½ααααααααααααααααααΆααααααΆαα’αααΈααααΉααααααα
ααα»αααααα ααΈααααααα αΆααΈαααα»ααααα αΆαααΎα:
- ααααα "subrecursive" αααααα½ααα·αα’αΆα
ααΆααα»αααΆαααα»αααΆαα½α
GROUP BY
. - ααα ααααΈααααα "ααΆααΆα" αααα’αΆα ααααΎα‘αΎααα·αααΆα αα·αα’αΆα αααα·ααα αααα»ααααα½ααααααΆαααααααααΆαααα
- ααααΎαα αααα»αααααααααααααΎα‘αΎααα·ααα·αα’αΆα ααΆα CTE ααΆαααα
ααΆααααΆαααα’ αααα αΆααΆααα’ααααααα·αααΆααΆααααα½ααααα»αααΆααααααααΆαα α αΌαα αΆααααααΎαααΈααΈαααα ααα
CTE αα αααα»αααααα recursive
αα ααΈααα αα·αααΆα ααααΎααΆαα
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