Académique Documents
Professionnel Documents
Culture Documents
GeneratingDepthbasedtreefromHierarchicalDatainMySQL(noCTEs)
HiFormanydaysIhavebeenworkingonthisprobleminMySQL,howeverIcannotfigureitout.Doanyofyouhavesuggestions?
Basically,Ihaveacategorytablewithdomainslike: id , name (nameofcategory),and parent (idofparentofthecategory).
ExampleData:
1Fruit0
2Apple1
3pear1
4FujiApple2
5AusApple2
6SydneyAPPLE5
....
Therearemanylevels,possiblymorethan3levels.Iwanttocreateansqlquerythatgroupsthedatasaccordingtohehierarchy:parent>child
>grandchild>etc.
Itshouldoutputthetreestructure,asfollows:
1Fruit0
^2Apple1
^4FujiApple2
5AusApple2
^6SydneyApple5
3pear1
CanIdothisusingasingleSQLquery?Thealternative,whichItriedanddoeswork,isthefollowing:
SELECT*FROMcategoryWHEREparent=0
Afterthis,Iloopthroughthedataagain,andselecttherowswhereparent=id.Thisseemslikeabadsolution.BecauseitismySQL,CTEscannot
beused.
mysql hierarchicaldata commontableexpression
editedJun16'12at1:49
KirkBroadhurstMar14'11at12:39
stackoverflow.com/questions/4048151/ orangepips Mar14'11at18:54
addedsomeadditionalinfotomyanswer:) JonBlack Mar15'11at1:53
4Answers
Youcandoitinasinglecallfromphptomysqlifyouuseastoredprocedure:
Examplecalls
mysql>callcategory_hier(1);
++++++
|cat_id|category_name|parent_cat_id|parent_category_name|depth|
++++++
|1|Location|NULL|NULL|0|
|3|USA|1|Location|1|
|4|Illinois|3|USA|2|
|5|Chicago|3|USA|2|
++++++
4rowsinset(0.00sec)
$sql=sprintf("callcategory_hier(%d)",$id);
Hopethishelps:)
Fullscript
Testtablestructure:
droptableifexistscategories;
createtablecategories
(
cat_idsmallintunsignednotnullauto_incrementprimarykey,
namevarchar(255)notnull,
parent_cat_idsmallintunsignednull,
key(parent_cat_id)
)
engine=innodb;
askedMar13'11at17:23
DavidManheim
bluedream
1,830
154
12
34
13
help
Testdata:
insertintocategories(name,parent_cat_id)values
('Location',null),
('USA',1),
('Illinois',2),
('Chicago',2),
('Color',null),
('Black',3),
('Red',3);
Procedure:
dropprocedureifexistscategory_hier;
delimiter#
createprocedurecategory_hier
(
inp_cat_idsmallintunsigned
)
begin
declarev_donetinyintunsigneddefault0;
declarev_depthsmallintunsigneddefault0;
createtemporarytablehier(
parent_cat_idsmallintunsigned,
cat_idsmallintunsigned,
depthsmallintunsigneddefault0
)engine=memory;
insertintohierselectparent_cat_id,cat_id,v_depthfromcategorieswherecat_id=
p_cat_id;
/*http://dev.mysql.com/doc/refman/5.0/en/temporarytableproblems.html*/
createtemporarytabletmpengine=memoryselect*fromhier;
whilenotv_donedo
ifexists(select1fromcategoriespinnerjoinhieronp.parent_cat_id=hier.cat_id
andhier.depth=v_depth)then
insertintohier
selectp.parent_cat_id,p.cat_id,v_depth+1fromcategoriesp
innerjointmponp.parent_cat_id=tmp.cat_idandtmp.depth=v_depth;
setv_depth=v_depth+1;
truncatetabletmp;
insertintotmpselect*fromhierwheredepth=v_depth;
else
setv_done=1;
endif;
endwhile;
select
p.cat_id,
p.nameascategory_name,
b.cat_idasparent_cat_id,
b.nameasparent_category_name,
hier.depth
from
hier
innerjoincategoriesponhier.cat_id=p.cat_id
leftouterjoincategoriesbonhier.parent_cat_id=b.cat_id
orderby
hier.depth,hier.cat_id;
droptemporarytableifexistshier;
droptemporarytableifexiststmp;
end#
Testruns:
delimiter;
callcategory_hier(1);
callcategory_hier(2);
SomeperformancetestingusingYahoogeoplanetplacesdata
droptableifexistsgeoplanet_places;
createtablegeoplanet_places
(
woe_idintunsignednotnull,
iso_codevarchar(3)notnull,
namevarchar(255)notnull,
langvarchar(8)notnull,
place_typevarchar(32)notnull,
parent_woe_idintunsignednotnull,
primarykey(woe_id),
key(parent_woe_id)
)
engine=innodb;
mysql>selectcount(*)fromgeoplanet_places;
++
|count(*)|
++
|5653967|
++
sothat's5.6millionrows(places)inthetablelet'sseehowtheadjacencylist
implementation/storedprocedurecalledfromphphandlesthat.
1recordsfetchedwithmaxdepth0in0.001921secs
250recordsfetchedwithmaxdepth1in0.004883secs
515recordsfetchedwithmaxdepth1in0.006552secs
822recordsfetchedwithmaxdepth1in0.009568secs
918recordsfetchedwithmaxdepth1in0.009689secs
1346recordsfetchedwithmaxdepth1in0.040453secs
5901recordsfetchedwithmaxdepth2in0.219246secs
6817recordsfetchedwithmaxdepth1in0.152841secs
8621recordsfetchedwithmaxdepth3in0.096665secs
18098recordsfetchedwithmaxdepth3in0.580223secs
238007recordsfetchedwithmaxdepth4in2.003213secs
Overalli'mprettypleasedwiththosecoldruntimesasIwouldn'tevenbegintoconsiderreturning
tensofthousandsofrowsofdatatomyfrontendbutwouldratherbuildthetreedynamically
fetchingonlyseverallevelspercall.Ohandjustincaseyouwerethinkinginnodbisslowerthan
myisamthemyisamimplementationItestedwastwiceasslowinallcounts.
Morestuffhere:http://pastie.org/1672733
Hopethishelps:)
editedApr2at21:00
answeredMar13'11at17:40
Nathanael
2,016
JonBlack
15
29
10.9k
29
33
6 fearnotitdoesnt.JonBlack Mar13'11at18:26
iamtryingthismethod,writingmyownone,thenmaybeiwillcheckouttheprocessingtime bluedream
Mar14'11at0:10
youcandosomeperformanceandstresstestingwithYahooGeoPlanetdatafoundhere:
developer.yahoo.com/geo/geoplanet/dataJonBlack Mar14'11at21:53
1 Coolsolution.Imodifieditabittosupportbuildingupthe"fullpath"forthename,andtoacceptNULLid
paramtoprintALLparentsandtheirchildren.Codeisatgist.github.com/jdmullin/9377818JeremyMullin
Mar5'14at22:20
TherearetwocommonwaysofstoringhierarchicaldatainanRDBMS:adjacencylists(which
youareusing)andnestedsets.Thereisaverygoodwriteupaboutthesealternativesin
ManagingHierarchicalDatainMySQL.Youcanonlydowhatyouwantinasinglequerywiththe
nestedsetmodel.However,thenestedsetmodelmakesitmoreworktoupdatethehierarchical
structure,soyouneedtoconsiderthetradeoffsdependingonyouroperationalrequirements.
editedOct30'11at20:47
answeredMar13'11at17:31
PaloEbermann
45.7k
80
TedHopp
141
155k
26
228
329
Youcan'tachievethisusingasinglequery.Yourhierarchicaldatamodelisineffectiveinthis
case.Isuggestyoutrytwootherwaysofstoringhierarchicaldatainadatabase:theMPTT
modelorthe"lineage"model.Usingeitherofthosemodelsallowsyoutodotheselectyouwantin
asinglego.
Hereisanarticlewithfurtherdetails:http://articles.sitepoint.com/article/hierarchicaldata
database
editedMar13'11at17:36
answeredMar13'11at17:30
CyberDude
4,794
12
6 @Martin:actuallyallothermajorDBMSsupportrecursiveCTEexceptMySQLa_horse_with_no_name
Mar14'11at12:39
34
Thelinearway:
Iamusingauglyfunctiontocreateatreeinasimplestringfield.
/topictitle
/001message1
/002message2
/002/001replytomessage2
/002/001/001/replytoreply
/003message3
etc...
thetablecanbeusedtoselectalltherowsinthetreeorderwithasimpleSQL
Query:
select*frommorum_messageswherem_topic=1234orderbym_linearasc
INSERT isjustselecttheparentlinear(andchildren)andcalculatethestringas
needed.
selectM_LINEARFROMforum_messagesWHEREm_topic=1234andM_LINEARLIKE'{0}/___'ORDER
BYM_LINEARDESClimit0,1
/*{0}m_linearoftheparentmessage*/
DELETE
issimpleasdeletethemessage,ordeletebylinearallrepliesoftheparent
one.
answeredMay4'12at9:39
MosheL
573