{"id":367,"date":"2008-01-07T16:31:10","date_gmt":"2008-01-07T15:31:10","guid":{"rendered":"http:\/\/soci.hu\/blog\/index.php\/2008\/01\/07\/sql-server-2008-ujdonsagok-10-hierarchyid-adattipus-1\/"},"modified":"2008-01-15T15:38:55","modified_gmt":"2008-01-15T14:38:55","slug":"sql-server-2008-ujdonsagok-10-hierarchyid-adattipus-1","status":"publish","type":"post","link":"https:\/\/soci.hu\/blog\/index.php\/2008\/01\/07\/sql-server-2008-ujdonsagok-10-hierarchyid-adattipus-1\/","title":{"rendered":"SQL Server 2008 \u00fajdons\u00e1gok 10. &#8211; HierarchyId adatt\u00edpus 1."},"content":{"rendered":"<p>Egy \u00e9rdekes t\u00edpussal kezd\u00fcnk most ismerkedni, amelyet kiss\u00e9 nehezen siker\u00fclt felfognom, tudjuk ezt be a koromnak. :)<\/p>\n<p>HierarchyId. Mit sugall ez a n\u00e9v? \u0150 egy olyan t\u00edpus, amely egy hierarchia, azaz egy fa egy adott pontj\u00e1t tudja megc\u00edmezni. Hogyan lehet rel\u00e1ci\u00f3s adatb\u00e1zisban f\u00e1t \u00e9p\u00edteni? Pl. rekurz\u00edv, \u00f6nhivatkoz\u00f3 t\u00e1bl\u00e1val, mint a Northwind adatb\u00e1zis Employees t\u00e1bl\u00e1ja, vagy az AdventureWorks adatb\u00e1zis HumanResources.Employee t\u00e1bl\u00e1ja. Ez ut\u00f3bbiban a ManagerID oszlop mutat a f\u0151n\u00f6k EmployeeID-j\u00e1ra.<\/p>\n<p>Az \u00edgy fel\u00e9p\u00edtett fa tetsz\u0151leges eleme jellemezhet\u0151 egy \u00fagynevezett <a href=\"http:\/\/www.cs.umb.edu\/~poneil\/ordpath.pdf\">OrdPath<\/a>-szal. Ebben a gyermekelemeknek sorrendje van, mint pl. az xml infosetben, \u00edgy a gyerekek megc\u00edmezhet\u0151k a sz\u00fcl\u0151 alatt sorsz\u00e1mukkal. 1\/2\/4 pl. a gy\u00f6k\u00e9r node 2. gyermek\u00e9nek a 4. gyerek\u00e9t jelenti. Az el\u0151bbi linken vizualiz\u00e1lj\u00e1k is, \u00e9rdemes megn\u00e9zni.<\/p>\n<p>Update: a link id\u0151nk\u00e9nt nem el\u00e9rhet\u0151, ez\u00e9rt <a href=\"\/blog\/wp-content\/uploads\/2008\/01\/ordpath.pdf\">t\u00fckr\u00f6ztem<\/a> a doksit.<\/p>\n<p>No, a HierarchyId egy olyan CLR t\u00edpus (ez az els\u0151 alkalom, hogy egy t\u00edpust .NET-ben \u00edrt meg az MS), amely egy OrdPath-ot k\u00e9pes let\u00e1rolni. A doksi ezt \u00edgy nem \u00edrja le, de \u00edgy tal\u00e1n k\u00f6nnyebb meg\u00e9rteni. Seg\u00edts\u00e9g\u00e9vel tulajdonk\u00e9ppen igen kompakt m\u00f3don le lehet t\u00e1rolni egy hierarchia node hely\u00e9t egy f\u00e1ban. Norm\u00e1l esetben pl. rekurz\u00edv CTE-vel j\u00e1rhatunk be egy hierarchi\u00e1t, hogy meghat\u00e1rozzuk az el\u00e9r\u00e9si \u00fatj\u00e1t egy node-nak. Ez el\u00e9g lass\u00fa persze, minden szinthez kell egy JOIN. Egy t\u00e1bl\u00e1ban HierarchyId oszlop seg\u00edts\u00e9g\u00e9vel minden egyes, a fa egy node-j\u00e1t reprezent\u00e1l\u00f3 sorhoz let\u00e1rolhatjuk a sor mint fa-node a hierarchi\u00e1ban elfoglalt hely\u00e9t, \u00edgy rekurzi\u00f3 n\u00e9lk\u00fcl is azonnal l\u00e1that\u00f3, hol foglal helyet a hierarchi\u00e1ban az adott sor (mint node).<\/p>\n<p>A HierarchyId felfoghat\u00f3 egyfajta denormaliz\u00e1l\u00e1si technik\u00e1nak is, hisz a hierarchia let\u00e1rolhat\u00f3 a m\u00e1r eml\u00edtett rel\u00e1ci\u00f3s m\u00f3don is. Ak\u00e1r egyszerre is lehet haszn\u00e1lni a kett\u0151t, de k\u00fcl\u00f6n-k\u00fcl\u00f6n is. Vannak esetek, amikor az egyik lesz hat\u00e9konyabb, van, amikor a m\u00e1sik. Ezeket majd k\u00e9s\u0151bbi r\u00e9szekben kielemzem, most egyel\u0151re l\u00e1ssunk egy\u00e1ltal\u00e1n egy p\u00e9ld\u00e1t, mi a csuda ez a t\u00edpus.<\/p>\n<p>Indul\u00f3 adatokat \u00fagy gy\u00e1rtok, hogy fogom az HumanResources.Employee t\u00e1bl\u00e1t, ami rel\u00e1ci\u00f3s m\u00f3don t\u00e1rol szervezeti hierarchi\u00e1t, \u00e9s ezt \u00e1talak\u00edtom HierarchyId reprezent\u00e1ci\u00f3ra (a p\u00e9lda a BOL-beli minta alaj\u00e1n k\u00e9sz\u00fclt).<\/p>\n<p>K\u00e9sz\u00edts\u00fcnk egy keskeny t\u00e1bl\u00e1t, hogy ne zavarjon meg minket a sok adat:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nselect EmployeeID, LoginID, ManagerID, Title \r\ninto HumanResources.EmployeeDemo \r\nfrom HumanResources.Employee;\r\n<\/pre>\n<p>Mit tal\u00e1lunk benne?<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nSELECT \r\nMgr.EmployeeID AS ManagerID, \r\nMgr.LoginID AS Manager,\r\n--Mgr.Title as ManagerTitle,\r\n \r\nEmp.EmployeeID AS EmployeeID, \r\nEmp.LoginID as Employee \r\n--Emp.Title as EmployeeTitle\r\n\r\nFROM HumanResources.EmployeeDemo AS Emp\r\nLEFT JOIN HumanResources.EmployeeDemo AS Mgr\r\nON Emp.ManagerID = Mgr.EmployeeID\r\nORDER BY ManagerID, EmployeeID\r\n<\/pre>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nManagerID   Manager                        EmployeeID  Employee\r\n----------- ------------------------------ ----------- ------------------------------\r\nNULL        NULL                           109         adventure-works\\ken0\r\n3           adventure-works\\roberto0       4           adventure-works\\rob0\r\n3           adventure-works\\roberto0       9           adventure-works\\gail0\r\n...\r\n3           adventure-works\\roberto0       270         adventure-works\\sharon0\r\n6           adventure-works\\david0         2           adventure-works\\kevin0\r\n6           adventure-works\\david0         46          adventure-works\\sariya0\r\n...\r\n6           adventure-works\\david0         272         adventure-works\\mary2\r\n7           adventure-works\\jolynn0        37          adventure-works\\simon0\r\n...\r\n<\/pre>\n<p>L\u00e1that\u00f3, hogy a 109-es sorsz\u00e1m\u00fa ken0 a f\u0151gy\u00f6k\u00e9r, a legnagyobb f\u0151n\u00f6k. A 3-as kisf\u0151n\u00f6k beosztottjai 4, 9. stb. sz\u00e1m\u00fa dolgoz\u00f3k.<\/p>\n<p>No, hozzunk l\u00e9tre egy \u00faj t\u00e1bl\u00e1t, ami ugyanezen adatokat tartalmazza, de most lesz minden node-ra egy k\u00f6zvetlen id-nk is:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\ncreate table HumanResources.NewOrg\r\n(\r\n  OrgNode hierarchyid,\r\n  EmployeeId int,\r\n  LoginID nvarchar(50),\r\n  ManagerID int, \r\n  Title nvarchar(100) \r\n  constraint PK_NewOrg_OrgNode primary key clustered (OrgNode)\r\n)\r\n<\/pre>\n<p>Az OrgNode nev\u0171 oszlop lett HierarchyId adatt\u00edpussal pluszk\u00e9nt felv\u00e9ve.<\/p>\n<p>Most j\u00f6n az adatok \u00e1tb\u0171v\u00f6l\u00e9se. A feladat, hogy rekurz\u00edvan j\u00e1rjuk be a hierarchi\u00e1t, \u00e9s menet k\u00f6zben gener\u00e1ljuk le a HierarchiId-ket. Azonban eml\u00e9kezz\u00fcnk r\u00e1, hogy az OrdPath sorrenddel dolgozik (benne van a nev\u00e9ben is, Order Path), \u00edgy be kellene vezetni valamif\u00e9le sorsz\u00e1moz\u00e1st, amely valamely (tetsz\u0151leges) szempont szerint sorbarendezi egy sz\u00fcl\u0151 (f\u0151n\u00f6k) gyermekeit (beosztottak). Nem nagy \u00fcgy, k\u00f6sz\u00f6nhet\u0151en a 2005-ben megjelent row_number f\u00fcggv\u00e9nynek. Egy seg\u00e9dt\u00e1bl\u00e1ban rakjuk \u00f6ssze a sorsz\u00e1mokat:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nCREATE TABLE #Children \r\n(\r\n  EmployeeId int,\r\n  ManagerID int,\r\n  Num int\r\n);\r\ngo\r\n\r\ncreate clustered index tmpind \r\non #Children(ManagerID, EmployeeID); \r\ngo\r\n\r\ninsert #Children (EmployeeID, ManagerID, Num)\r\nselect EmployeeID, ManagerID, row_number() \r\nover (partition by ManagerID order by EmployeeID) \r\nfrom HumanResources.EmployeeDemo;\r\ngo\r\n<\/pre>\n<p>F\u0151n\u00f6k\u00f6k (partition by ManagerID) szerint csoportos\u00edtva gener\u00e1ljuk a sorsz\u00e1mokat a beosztottakhoz, EmpoyeeID szerint sorbarendezve (order by EmployeeID). Val\u00f3j\u00e1ban t\u00f6k mindegy, mi szerint rendez\u00fcnk, hisz az eredeti t\u00e1bl\u00e1ban nem is volt sorrend, ne felejts\u00fck el, a rel\u00e1ci\u00f3s adatb\u00e1zisok halmazokkal, \u00e9s nem sorrendezett adatokkal dolgoznak. Most viszont az OrdPath miatt ki kell alak\u00edtani valami sorrendet.<br \/>\nKukkantsunk bele a gener\u00e1lt t\u00e1bl\u00e1ba:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nselect ManagerId, EmployeeID, Num \r\nfrom #Children\r\norder by ManagerId, Num\r\n<\/pre>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nManagerId   EmployeeID  Num\r\n----------- ----------- -----------\r\nNULL        109         1\r\n3           4           1\r\n3           9           2\r\n3           11          3\r\n3           158         4\r\n3           263         5\r\n3           267         6\r\n3           270         7\r\n6           2           1\r\n6           46          2\r\n6           106         3\r\n<\/pre>\n<p>Az els\u0151 sor HierarchyId-je \/ lenne, \u0151 a gy\u00f6k\u00e9r. A m\u00e1sodik sor HierarchyId-j\u00e1t nem tudjuk kap\u00e1sb\u00f3l megmondani, mert ahhoz vissza kellene menni, hogy ki a 3-as ember f\u0151n\u00f6ke, eg\u00e9szen a gy\u00f6k\u00e9rig visszal\u00e9pdelve, \u00e9s akkor a sorrend alapj\u00e1n m\u00e1r lehetne k\u00e9pezni egy OrdPath-ot. Nos, itt az ideje, hogy legener\u00e1ljuk az id-kat, egy rekurz\u00edv CTE-vel:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nwith paths(Path, EmployeeID) \r\nas \r\n(\r\n--Gy\u00f6k\u00e9r\r\nselect \r\ncast(&#039;\/&#039; as hierarchyid) as OrgNode, \r\nEmployeeID \r\nfrom #Children\r\nwhere ManagerID is null \r\n\r\nunion all\r\n\r\n--A gy\u00f6k\u00e9r alatti \u00f6sszes szint\r\nselect \r\ncast(p.Path.ToString() + cast(c.Num as varchar(30)) + &#039;\/&#039; AS hierarchyid), \r\nc.EmployeeID\r\nfrom #Children as c\r\njoin Paths as p \r\non c.ManagerID = p.EmployeeID \r\n)\r\ninsert HumanResources.NewOrg \r\n(OrgNode, O.EmployeeID, O.LoginID, O.ManagerID, O.Title)\r\nselect P.path, O.EmployeeID, O.LoginID, O.ManagerID, O.Title\r\nfrom HumanResources.EmployeeDemo as O \r\njoin Paths as P\r\non O.EmployeeID = P.EmployeeID;\r\ngo\r\n<\/pre>\n<p>A HierarchyId-t ez a sor rakja \u00f6ssze:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\ncast(p.Path.ToString() + cast(c.Num as varchar(30)) + &#039;\/&#039; AS hierarchyid)\r\n<\/pre>\n<p>Fogjuk a sz\u00fcl\u0151 el\u00e9r\u00e9si \u00fatj\u00e1t (p.Path), \u00e1talak\u00edtjuk stringg\u00e9 (.ToString(), el\u00e9g .NET-es? :), hozz\u00e1f\u0171zz\u00fck a gyerek sorsz\u00e1m\u00e1t \u00e9s egy z\u00e1r\u00f3 \/ jelet (+ cast(c.Num as varchar(30)) + &#8216;\/&#8217;). Az ered\u0151 stringet vissza cast-oljuk HierarchyId-v\u00e1. Lehetne m\u00e1sk\u00e9pp is, mert van p\u00e1r sz\u00fcl\u0151-gyerek kezel\u0151 met\u00f3dusa a t\u00edpusnak, de err\u0151l majd egy m\u00e1sik r\u00e9szben \u00edrok.<\/p>\n<p>Aki nem l\u00e1tott m\u00e9g CTE-et, a union feletti r\u00e9sz egyszer fut le, az gener\u00e1lja le a gy\u00f6k\u00e9r sort, a alatta lev\u0151 r\u00e9sz pedig mindig az el\u0151z\u0151 l\u00e9p\u00e9s \u00e1ltal gener\u00e1lt gyerek sorokra hajt\u00f3dik v\u00e9gre, am\u00edg be nem j\u00e1rja az \u00f6sszes szintet. Ez ugye egy rekurz\u00edv self-join.<\/p>\n<p>N\u00e9zz\u00fcnk bele a m\u0171be:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nSELECT OrgNode.ToString() AS LogicalNode, * \r\nFROM HumanResources.NewOrg \r\nORDER BY LogicalNode;\r\n<\/pre>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nLogicalNode                    OrgNode                          EmployeeId  LoginID                        ManagerID   Title\r\n------------------------------ -------------------------------- ----------- ------------------------------ ----------- ------------------------------\r\n\/                              0x                               109         adventure-works\\ken0           NULL        Chief Executive Officer\r\n\/1\/                            0x58                             6           adventure-works\\david0         109         Marketing Manager\r\n\/1\/1\/                          0x5AC0                           2           adventure-works\\kevin0         6           Marketing Assistant\r\n\/1\/2\/                          0x5B40                           46          adventure-works\\sariya0        6           Marketing Specialist\r\n\/1\/3\/                          0x5BC0                           106         adventure-works\\mary0          6           Marketing Specialist\r\n\/1\/4\/                          0x5C20                           119         adventure-works\\jill0          6           Marketing Specialist\r\n\/1\/5\/                          0x5C60                           203         adventure-works\\terry0         6           Marketing Specialist\r\n\/1\/6\/                          0x5CA0                           269         adventure-works\\wanida0        6           Marketing Assistant\r\n\/1\/7\/                          0x5CE0                           271         adventure-works\\john5          6           Marketing Specialist\r\n\/1\/8\/                          0x5D10                           272         adventure-works\\mary2          6           Marketing Assistant\r\n\/2\/                            0x68                             12          adventure-works\\terri0         109         Vice President of Engineering\r\n\/2\/1\/                          0x6AC0                           3           adventure-works\\roberto0       12          Engineering Manager\r\n\/2\/1\/1\/                        0x6AD6                           4           adventure-works\\rob0           3           Senior Tool Designer\r\n\/2\/1\/2\/                        0x6ADA                           9           adventure-works\\gail0          3           Design Engineer\r\n\/2\/1\/3\/                        0x6ADE                           11          adventure-works\\jossef0        3           Design Engineer\r\n\/2\/1\/4\/                        0x6AE1                           158         adventure-works\\dylan0         3           Research and Development Manag\r\n\/2\/1\/4\/1\/                      0x6AE158                         79          adventure-works\\diane1         158         Research and Development Engin\r\n\/2\/1\/4\/2\/                      0x6AE168                         114         adventure-works\\gigi0          158         Research and Development Engin\r\n<\/pre>\n<p>Sz\u00e9p, mi? De mi a csud\u00e1ra j\u00f3 ez? Mi\u00e9rt j\u00f3, hogy meg van minden sor OrdPath-ja (HierarchyId-je)? A k\u00f6vetkez\u0151 r\u00e9szb\u0151l kider\u00fcl. Stay tuned. :)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Egy \u00e9rdekes t\u00edpussal kezd\u00fcnk most ismerkedni, amelyet kiss\u00e9 nehezen siker\u00fclt felfognom, tudjuk ezt be a koromnak. :) HierarchyId. Mit sugall ez a&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,4,58],"tags":[],"class_list":["post-367","post","type-post","status-publish","format-standard","hentry","category-adatbazisok","category-szakmai-elet","category-sql-server-2008"],"_links":{"self":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/367","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=367"}],"version-history":[{"count":0,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/367\/revisions"}],"wp:attachment":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=367"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=367"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=367"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}