Amin'ny rafitra ERP sarotra sampana maro no manana toetra ara-jeografikarehefa milahatra ny zavatra homogeneous hazo fifandraisana amin'ny razana - izany no firafitry ny fandaminana ny orinasa (ireo sampana rehetra, sampana ary vondrona miasa), sy ny katalaogin'ny entana, sy ny sehatry ny asa, ary ny jeografia ny varotra,...
Raha ny marina dia tsy misy izany
Misy fomba maro hitahirizana hazo toy izany ao amin'ny DBMS, saingy ankehitriny isika dia hifantoka amin'ny safidy tokana:
CREATE TABLE hier(
id
integer
PRIMARY KEY
, pid
integer
REFERENCES hier
, data
json
);
CREATE INDEX ON hier(pid); -- Π½Π΅ Π·Π°Π±ΡΠ²Π°Π΅ΠΌ, ΡΡΠΎ FK Π½Π΅ ΠΏΠΎΠ΄ΡΠ°Π·ΡΠΌΠ΅Π²Π°Π΅Ρ Π°Π²ΡΠΎΡΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΈΠ½Π΄Π΅ΠΊΡΠ°, Π² ΠΎΡΠ»ΠΈΡΠΈΠ΅ ΠΎΡ PK
Ary raha mibanjina ny halalin'ny ambaratongam-pahefana ianao, dia miandry amim-paharetana ny hahita ny fahombiazan'ny fomba fiasanao amin'ny rafitra toy izany.
Andeha hojerentsika ny olana mahazatra mitranga, ny fampiharana azy amin'ny SQL, ary miezaka manatsara ny fahombiazany.
#1. Hatraiza ny halalin'ny lavaka bitro?
Aoka ho azo antoka, hanaiky fa io rafitra io dia haneho ny fanambanin'ny sampana amin'ny firafitry ny fikambanana: sampana, sampana, sehatra, sampana, vondrona miasa... - na inona na inona iantsoanao azy.
Voalohany, andao hamorona ny 'hazo' misy singa 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;
Andeha isika hanomboka amin'ny asa tsotra indrindra - fitadiavana mpiasa rehetra miasa ao anatin'ny sehatra iray manokana, na amin'ny resaka ambaratongam-pahefana - mahita ny zanaky ny node rehetra. Tsara ihany koa ny mahazo ny "halalin'ny" ny taranaka ... Mety ilaina izany rehetra izany, ohatra, ny fananganana karazana
Hilamina ny zava-drehetra raha toa ka misy ambaratonga roa amin'ireo taranaka ireo ary ao anatin'ny folo ny isa, fa raha mihoatra ny 5 ny ambaratonga, ary efa am-polony ny taranaka dia mety hisy ny olana. Andeha hojerentsika ny fomba nanoratana (sy miasa) ny safidy fikarohana midina amin'ny hazo. Fa aloha, andeha hojerentsika hoe inona no node ho mahaliana indrindra amin'ny fikarohana ataontsika.
Ny be indrindra "lalina" zana-kazo:
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}
...
Ny be indrindra "malalaka" zana-kazo:
...
SELECT
path[1] id
, count(*)
FROM
T
GROUP BY
1
ORDER BY
2 DESC;
id | count
------------
5300 | 30
450 | 28
1239 | 27
1573 | 25
Ho an'ireo fanontaniana ireo dia nampiasa ny mahazatra izahay recursive JOIN:
Mazava ho azy, miaraka amin'ity modely fangatahana ity ny isan'ny famerimberenana dia hifanaraka amin'ny fitambaran'ny isan'ny taranaka (ary misy am-polony maromaro amin'izy ireo), ary mety haka loharanon-karena lehibe izany, ary, vokatr'izany, fotoana.
Andeha hojerentsika ny zana-kazo "lehibe indrindra":
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;
Araka ny efa nampoizina dia hitanay avokoa ny rakitra 30. Saingy nandany ny 60% tamin'ny fotoana rehetra tamin'izany izy ireo - satria nanao fikarohana 30 tao amin'ny index. Azo atao ve ny manao kely kokoa?
Fanamarinana betsaka amin'ny fanondroana
Mila manao fangatahana fanondroana manokana ho an'ny node tsirairay ve isika? Hita fa tsia - afaka mamaky avy amin'ny fanondroana isika mampiasa fanalahidy maromaro indray mandeha amin'ny antso iray miaraka amin'ny fanampian'ny = ANY(array)
.
Ary isaky ny vondrona famantarana toy izany dia afaka maka ny ID rehetra hita tamin'ny dingana teo aloha amin'ny "nodes". Izany hoe, isaky ny dingana manaraka isika mitady ny taranaka rehetra amin'ny ambaratonga iray indray mandeha.
Izao ihany no olana, amin'ny fifantenana miverimberina dia tsy afaka miditra amin'ny fanontaniana misy akany ianao, saingy tsy maintsy misafidy izay hita tamin'ny ambaratonga teo aloha ihany isika... Hita fa tsy azo atao ny manao fangatahana nested ho an'ny fifantenana manontolo, fa ho an'ny saha manokana dia azo atao izany. Ary ity saha ity dia mety ho array ihany koa - izay no ilaintsika ampiasaina ANY
.
Toa adala kely izany, saingy amin'ny diagram dia tsotra ny zava-drehetra.
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;
Ary eto dia tsy ny zava-dehibe indrindra mandresy in-1.5 amin'ny fotoana, ary nalaintsika vitsy kokoa ny buffer, satria 5 ihany no antso ho an'ny index fa tsy 30!
Ny tombony fanampiny dia ny hoe aorian'ny tsy fisian'ny farany, ny mpamantatra dia hijanona ho baiko amin'ny "ambaratonga".
famantarana node
Ny fiheverana manaraka izay hanampy amin'ny fanatsarana ny fampisehoana dia β "ravina" dia tsy afaka miteraka, izany hoe ho azy ireo dia tsy ilaina ny mijery "midina" mihitsy. Amin'ny fandrafetana ny asantsika dia midika izany fa raha manaraka ny rojo sampan-draharaha isika ary mahatratra mpiasa iray, dia tsy ilaina ny mijery lavitra an'io sampana io.
Andeha isika hiditra ao amin'ny latabatry FANAMPINY boolean
- saha, izay hilaza amintsika avy hatrany raha βnodeβ io fidirana manokana ao amin'ny hazontsika io - izany hoe mety hanana taranaka mihitsy.
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 ΠΌΡ.
Mahafinaritra! Hita fa maherinβny 30% monja aminβireo singa hazo rehetra no manana taranaka.
Andeha isika hampiasa mekanika hafa kely - fifandraisana amin'ny ampahany miverimberina LATERAL
, izay ahafahantsika miditra avy hatrany amin'ny sahan'ny "latabatra" miverimberina ary mampiasa fiasa mitambatra miaraka amin'ny fepetra sivana mifototra amin'ny node mba hampihenana ny fitambaran'ny fanalahidy:
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;
Afaka nampihena ny antso fanondroana indray izahay ary nandresy mihoatra ny in-2 tamin'ny volume proofread.
#2. Andao hiverina amin'ny fotony
Ity algorithm ity dia ilaina raha toa ka mila manangona rakitsoratra ho an'ny singa rehetra "any amin'ny hazo" ianao, raha mitazona fampahalalana momba ny takelaka loharano (ary miaraka amin'ny famantarana inona) nahatonga azy ho tafiditra ao amin'ny santionany - ohatra, mba hamoronana tatitra famintinana. miaraka amin'ny aggregation ho nodes.
Ity manaraka ity dia tokony horaisina ho porofon'ny hevitra fotsiny, satria ny fangatahana dia lasa sarotra be. Fa raha manjaka ny angon-drakitrao dia tokony hieritreritra ny hampiasa teknika mitovy amin'izany ianao.
Andeha isika hanomboka amin'ny fanambarana tsotra roa:
- Ny rakitra mitovy amin'ny angon-drakitra Aleo mamaky azy indray mandeha monja.
- Firaketana avy amin'ny angon-drakitra Mahomby kokoa ny mamaky andianynoho ny irery.
Andeha isika hanandrana hanorina ny fangatahana ilaintsika.
dingana 1
Mazava ho azy, rehefa manomboka ny recursion (aiza isika raha tsy misy izany!) Tsy maintsy manaisotra ny rakitsoratry ny ravin-kazo isika mifototra amin'ny andiana famantarana voalohany:
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
...
Raha toa hafahafa amin'ny olona iray fa ny "set" dia voatahiry ho tady fa tsy array, dia misy fanazavana tsotra momba izany. Misy fiasa "gluing" mitambatra ho an'ny tady string_agg
, fa tsy ho an'ny array. Na dia izy aza
dingana 2
Ankehitriny dia hahazo andiana ID fizarana izay mila vakiana bebe kokoa. Saika adika matetika amin'ny rakitsoratra samihafa amin'ny andiany voalohany izy ireo - izany no hataontsika vondrona azy ireo, sady mitahiry vaovao momba ny ravin-kazo.
Fa olana telo no miandry antsika:
- Ny ampahany "subrecursive" amin'ny fangatahana dia tsy afaka mirakitra fiasa mitambatra
GROUP BY
. - Ny fanondroana "tabilao" miverimberina dia tsy azo atao amin'ny subquery misy akany.
- Ny fangatahana ao amin'ny ampahany miverimberina dia tsy misy CTE.
Soa ihany fa ireo olana rehetra ireo dia mora vahana. Andeha isika hanomboka amin'ny farany.
CTE amin'ny ampahany recursive
Here so tsy miasa:
WITH RECURSIVE tree AS (
...
UNION ALL
WITH T (...)
SELECT ...
)
Ary izany dia miasa, ny fononteny no manova ny fiovana!
WITH RECURSIVE tree AS (
...
UNION ALL
(
WITH T (...)
SELECT ...
)
)
Fanontaniana miompana amin'ny "latabatra" miverimberina
Hmm... Tsy azo idirana amin'ny subquery ny CTE miverimberina. Saingy mety ho ao anatin'ny CTE izany! Ary ny fangatahana nested dia efa afaka miditra amin'ity CTE ity!
GROUP BY anatiny recursion
Tsy mahafinaritra izany, saingy ... Manana fomba tsotra izahay hanahafana ny GROUP BY mampiasa DISTINCT ON
ary ny fiasan'ny varavarankely!
SELECT
(rec).pid id
, string_agg(chld::text, ',') chld
FROM
tree
WHERE
(rec).pid IS NOT NULL
GROUP BY 1 -- Π½Π΅ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ!
Ary izao no fomba fiasa!
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
Hitantsika izao ny antony nahatonga ny ID nomerika ho lahatsoratra - mba hahafahan'izy ireo mitambatra miaraka amin'ny faingo!
dingana 3
Ho an'ny famaranana dia tsy manana na inona na inona izahay:
- mamaky ny firaketana "fizarana" mifototra amin'ny andiana ID mivondrona izahay
- ampitahainay amin'ny "set" amin'ny takelaka tany am-boalohany ny fizarana nalaina
- "manitarana" ny tady napetraka mampiasa
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;