è€é㪠ERP ã·ã¹ãã ã®å Žå å€ãã®ãšã³ãã£ãã£ã¯éå±€çãªæ§è³ªãæã£ãŠããŸãåçš®ã®ç©äœã䞊ã¶ãš ç¥å ãšåå«ã®é¢ä¿ã®ããªãŒ - ããã¯äŒæ¥ã®çµç¹æ§é (ããããã¹ãŠã®æ¯åºãéšéãäœæ¥ã°ã«ãŒã)ãååã®ã«ã¿ãã°ãäœæ¥é åã販売ãã€ã³ãã®å°çãªã©ã§ãã
å®éã«ã¯äœããããŸãã
ãã®ãããªããªãŒã DBMS ã«ä¿åããã«ã¯å€ãã®æ¹æ³ããããŸãããä»æ¥ã¯ XNUMX ã€ã®ãªãã·ã§ã³ã ãã«çŠç¹ãåœãŠãŸãã
CREATE TABLE hier(
id
integer
PRIMARY KEY
, pid
integer
REFERENCES hier
, data
json
);
CREATE INDEX ON hier(pid); -- Ме забÑваеЌ, ÑÑП FK Ме пПЎÑазÑÐŒÐµÐ²Ð°ÐµÑ Ð°Ð²ÑПÑПзЎаМОе ОМЎекÑа, в ПÑлОÑОе ÐŸÑ PK
ãããŠãããªããéå±€ã®æ·±éšãèŠããŠããéããã®ãããªæ§é ã«å¯Ÿããããªãã®ãçŽ æŽãªãããæ¹ãã©ãã»ã©å¹æçã§ãªãã®ããèŸæ±åŒ·ãåŸ ã£ãŠããŸãã
çºçããå
žåçãªåé¡ãšãã® SQL ã§ã®å®è£
ãèŠãŠãããã©ãŒãã³ã¹ã®åäžãè©Šã¿ãŠã¿ãŸãããã
#1. ãŠãµã®ã®ç©Žã®æ·±ãã¯ã©ããããã§ããïŒ
念ã®ããã«èšã£ãŠãããŸããããã®æ§é ã¯çµç¹æ§é ã«ãããéšéã®åŸå±ãåæ ãããã®ã§ããããšãåãå
¥ããŸããã: éšéãéšéãã»ã¯ã¿ãŒãæ¯ç€Ÿãäœæ¥ã°ã«ãŒããªã©ãã©ã®ããã«åŒãã§ãæ§ããŸããã
ãŸãã10 åã®èŠçŽ ãããªããããªãŒããçæããŸããã
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 以å ã®å Žåã¯åé¡ãããŸããããã¬ãã«ã XNUMX ãè¶ ãããã§ã«æ°åã®åå«ãããå Žåã¯ãåé¡ãçºçããå¯èœæ§ããããŸãã åŸæ¥ã®ããªãŒã®äžäœæ€çŽ¢ãªãã·ã§ã³ãã©ã®ããã«èšè¿°ãããã (ãããŠåäœããã) ãèŠãŠã¿ãŸãããã ãã ããæåã«ãã©ã®ããŒãã調æ»ã«ãšã£ãŠæãèå³æ·±ãããå€æããŸãããã
ãã¹ã "æ·±ã" ãµãããªãŒ:
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% ãããã«è²»ããããŸããã ãã£ãšå°ãªãããããšã¯å¯èœã§ããïŒ
ã€ã³ããã¯ã¹ã«ããäžæ¬æ ¡æ£
ããŒãããšã«åå¥ã®ã€ã³ããã¯ã¹ ã¯ãšãªãäœæããå¿
èŠããããŸãã? ããããã€ã³ããã¯ã¹ããèªã¿åãããšãã§ããããšãããããŸãã XNUMX åã®é話ã§è€æ°ã®ããŒãåæã«äœ¿çšãã çµç± = ANY(array)
.
ãããŠããã®ãããªèå¥åã®ã°ã«ãŒãããšã«ãåã®ã¹ãããã§ãããŒããã«ãã£ãŠèŠã€ãã£ããã¹ãŠã® ID ãååŸã§ããŸãã ã€ãŸãã次ã®åã¹ãããã§ã ç¹å®ã®ã¬ãã«ã®ãã¹ãŠã®åå«ãäžåºŠã«æ€çŽ¢ãã.
ãã ããããåé¡ãªã®ã§ããã ååž°çéžæã§ã¯ããã¹ããããã¯ãšãªã§ããèªäœã«ã¢ã¯ã»ã¹ããããšã¯ã§ããŸãããããããåã®ã¬ãã«ã§èŠã€ãã£ããã®ã ããäœããã®æ¹æ³ã§éžæããå¿
èŠããããŸã...éžæå
šäœã«å¯ŸããŠãã¹ããããã¯ãšãªãäœæããããšã¯äžå¯èœã§ããããã®ç¹å®ã®ãã£ãŒã«ãã«å¯ŸããŠã¯å¯èœã§ããããšãããããŸãã ãããŠããã®ãã£ãŒã«ãã¯é
åã«ããããšãã§ããŸããããã䜿çšããå¿
èŠããããŸãã 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;
ã€ã³ããã¯ã¹åŒã³åºããããã« XNUMX ã€æžããããšãã§ããŸããã åºæ¥é«ã§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
ããã§ãããã«èªã¿åãå¿ èŠãããã»ã¯ã·ã§ã³ ID ã®ã»ãããååŸããŸãã ã»ãšãã©ã®å Žåããããã¯å ã®ã»ããã®ç°ãªãã¬ã³ãŒãã«è€è£œãããŸãã ããããã°ã«ãŒãåããããœãŒã¹ã®èã«é¢ããæ å ±ãä¿æããªããã
ãããããã㧠XNUMX ã€ã®åé¡ãåŸ ã¡æ§ããŠããŸãã
- ã¯ãšãªã®ãéšåååž°ãéšåã«ã¯ã次ã®ãããªéèšé¢æ°ãå«ããããšã¯ã§ããŸããã
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
äžæå¿«ã§ã¯ãããŸãã... ã䜿çšã㊠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 ãããã¹ãã«å€æãããçç±ãããããŸããããã®çµæãæ°å€ 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;