{"id":393,"date":"2008-01-14T18:41:03","date_gmt":"2008-01-14T17:41:03","guid":{"rendered":"http:\/\/soci.hu\/blog\/index.php\/2008\/01\/14\/sql-server-2008-ujdonsagok-12-hierarchyid-adattipus-4\/"},"modified":"2008-01-25T10:24:52","modified_gmt":"2008-01-25T09:24:52","slug":"sql-server-2008-ujdonsagok-12-hierarchyid-adattipus-4","status":"publish","type":"post","link":"https:\/\/soci.hu\/blog\/index.php\/2008\/01\/14\/sql-server-2008-ujdonsagok-12-hierarchyid-adattipus-4\/","title":{"rendered":"SQL Server 2008 \u00fajdons\u00e1gok 13. &#8211; HierarchyID adatt\u00edpus 4."},"content":{"rendered":"<p>P\u00e1r z\u00e1r\u00f3 gondolat a t\u00edpusr\u00f3l.<\/p>\n<p>Eddig mindig arra haszn\u00e1ltam, hogy egy hierarchi\u00e1ban a node-okhoz legyen k\u00f6zvetlen pointer\u00fcnk, ezzel egyes lek\u00e9rdez\u00e9seket jelent\u0151sen fel lehetett gyors\u00edtani. Ennek ellen\u00e9re ez a t\u00edpus nem m\u00e1s, mint egy nagyon t\u00f6m\u00f6r, sz\u00e1munkra el\u00e9r\u00e9si \u00fatk\u00e9nt \u00e9rtelmezhet\u0151 adatot t\u00e1rol\u00f3 valami. Hogy ez az el\u00e9r\u00e9si \u00fat passzoljon a t\u00e9nyleges hierarchi\u00e1hoz csakis a mi felel\u0151ss\u00e9g\u00fcnk, az \u00e9gvil\u00e1gon senki nem fogja biztos\u00edtani, hogy a HiearchyID-ben t\u00e1rolt adatnak b\u00e1rmi k\u00f6ze is van a val\u00f3s\u00e1ghoz. Nem olyan, mint pl. egy foreign key-jel v\u00e9dett kapcsolat, ahol az\u00e9rt cs\u00f6kken a baromkod\u00e1s es\u00e9lye.<br \/>\nA t\u00edpus igen kompakt, p\u00e1r b\u00e1jtot vesz csak ig\u00e9nybe a t\u00e1rol\u00e1sa, m\u00e9g nagyon nagy f\u00e1k eset\u00e9n is. Ha kicsit ut\u00e1na akarunk n\u00e9zni, hogyan implement\u00e1lt\u00e1k, akkor reflectorral meg kell nyitni a Microsoft.SqlServer.Types.dll assemblyt a GAC-b\u00f3l, ebben vannak a CLR SQL t\u00edpusok implement\u00e1lva (a t\u00f6bbi is, amelyekr\u0151l a k\u00e9s\u0151bbi cikkekben m\u00e9g lesz sz\u00f3).<\/p>\n<p>A File Disassembler pluginnal teljes eg\u00e9sz\u00e9ben vissza lehet fejteni forr\u00e1sk\u00f3dra, kicsit n\u00e9zz\u00fcnk bele. Hab\u00e1r C++\/CLI-ben \u00edrt\u00e1k, C#-k\u00e9nt mutatok be egy-k\u00e9t r\u00e9szletet, m\u00e9gha \u00edgy p\u00e1r dolog cs\u00fanya is (nincs const a C#-ban pl.). Maga a t\u00edpus \u00edgy van deklar\u00e1lva:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n&#x5B;SqlUserDefinedType(Format.UserDefined, IsByteOrdered=true, MaxByteSize=0x37c, Name=&quot;SqlHierarchyId&quot;)]\r\npublic class SqlHierarchyId : IBinarySerialize, INullable\r\n{\r\n    \/\/ Fields\r\n    private OrdPath ordpath;\r\n...\r\n}\r\n<\/pre>\n<p>Mivel a t\u00edpus implement\u00e1lja az IBinarySerialize interf\u00e9szt, amikor le kell t\u00e1rolni a t\u00edpus adatait, a Write met\u00f3dust h\u00edvja meg a szerver. Ennek l\u00e9nyegi r\u00e9sze:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic void Write(BinaryWriter w)\r\n{\r\n    w.Write(this.ordpath.m_bytes, 0, (this.ordpath.m_bitLength + 7) \/ 8);\r\n}\r\n<\/pre>\n<p>Azaz az OrdPath m_bytes adattagja, ennek bin\u00e1ris szerkezete d\u00f6nti el, hogy indexel\u00e9sn\u00e9l hogyan viselkedik a t\u00edpus. Ezt \u00fcgyesen \u00fagy rakt\u00e1k \u00f6ssze, hogy m\u00e9lys\u00e9gi m\u00f3don rendezze az \u00e1ltala reprezent\u00e1lt f\u00e1t.<br \/>\nL\u00e1that\u00f3an a l\u00e9nyeg igaz\u00e1b\u00f3l az OrdPath t\u00edpusban van, az SqlHieararchyID csak egy facade hozz\u00e1. <\/p>\n<p>Az OrdPath bel\u00fclr\u0151l m\u00e1r bonyolultabb:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\ninternal class OrdPath\r\n{\r\n    \/\/ Fields\r\n    public ushort m_bitLength;\r\n    public ushort&#x5B;] m_bitOffsets;\r\n    public byte&#x5B;] m_bytes;\r\n    public uint modopt(IsLong) m_level;\r\n    public uint modopt(IsLong) m_parentBitLengthIndex;\r\n    public uint modopt(IsLong) m_parsedLevels;\r\n    public SubType m_subType;\r\n...\r\n<\/pre>\n<p>Amikor stringb\u0151l k\u00e9peznek HierarchyID-t, akkor is az OrdPath dolgozik (Parse() h\u00edv\u00e1s vagy SQL CAST vagy CONVERT). SqlHiearchyId.Parse:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic static SqlHierarchyId Parse(SqlString input)\r\n{\r\n    string chDottedString = input.Value;\r\n...  \r\n    SqlHierarchyId id = new SqlHierarchyId();\r\n    id.ordpath = &lt;strong&gt;new OrdPath(chDottedString);&lt;\/strong&gt;\r\n    return id;\r\n}\r\n<\/pre>\n<p>Akit \u00e9rdekel, az OrdPath konstruktorban megn\u00e9zheti a konkr\u00e9t bitkolb\u00e1szol\u00e1st, az m\u00e1r t\u00fal hossz\u00fa, hogy itt kielemezzem.<\/p>\n<p>\u00c9rdemes m\u00e9g megn\u00e9zn\u00fck, hogyan kell az adatokat m\u00f3dos\u00edtani, a f\u00e1t kezelni a HierarchyID jelenl\u00e9t\u00e9ben.<\/p>\n<p>Csak k\u00f3stol\u00f3k\u00e9nt mutatok egy p\u00e9ld\u00e1t, amiben J\u00f3zsit el\u0151l\u00e9ptett\u00e9k, \u00faj f\u0151n\u00f6k\u00f6t kap a hierarchia magasabb szintj\u00e9n.<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nbegin tran\r\n\r\ndeclare @jozsi hierarchyid = \r\n(select OrgNode \r\nfrom HumanResources.NewOrg \r\nwhere LoginID = &#039;adventure-works\\dylan0&#039;) --2\/1\/4\r\n\r\ndeclare @jozsiujfonoke hierarchyid = \r\n(select OrgNode \r\nfrom HumanResources.NewOrg \r\nwhere LoginID = &#039;adventure-works\\david0&#039;) --\/1\/\r\n\r\nselect \r\n@jozsi.ToString() Jozsi, --\/2\/1\/4\/\r\n@jozsiujfonoke.ToString() JozsiUjFonoke, -- \/1\/\r\n@jozsiujfonoke.GetDescendant(null, null).ToString() ElsoBeosztott, --\/1\/1\/\r\n@jozsiujfonoke.GetDescendant(null, \r\n  @jozsiujfonoke.GetDescendant(null, null)).ToString() ElsoBeosztottElotti --\/1\/0\/\r\n\r\nupdate HumanResources.NewOrg\r\nset OrgNode = @jozsiujfonoke.GetDescendant(\r\n  null, @jozsiujfonoke.GetDescendant(null, null))\r\nwhere OrgNode = @jozsi\r\n\r\nrollback tran\r\n<\/pre>\n<p>Az eg\u00e9szben a GetDescendant met\u00f3dus a kulcs. Ez sokf\u00e9lek\u00e9ppen tud visszaadni gyereket, att\u00f3l f\u00fcgg\u0151en, hogy milyen k\u00e9t param\u00e9tert kap. A fontosabb esetek:<\/p>\n<p>1. If parent is not NULL, and both child1 and child2 are NULL, returns a child of parent.<\/p>\n<p>2. If parent and child1 are not NULL, and child2 is NULL, returns a child of parent greater than child1.<\/p>\n<p>3. If parent and child2 are not NULL and child1 is NULL, returns a child of parent less than child2.<\/p>\n<p>4. If parent, child1, and child2 are all not NULL, returns a child of parent greater than child1 and less than child2.<\/p>\n<p>Az ElsoBeosztott az 1. szab\u00e1ly alapj\u00e1n k\u00e9peztetett, az a child szemmel l\u00e1that\u00f3an az els\u0151 jelenti (legal\u00e1bbis most). Azt\u00e1n a 3. szab\u00e1ly \u00e9rtelm\u00e9ben lek\u00e9rtem az ElsoBeosztottElotti id-t. Ide poz\u00edcion\u00e1lom \u00fajra az update seg\u00edts\u00e9g\u00e9vel J\u00f3zsit, \u00edgy \u0151 lesz az els\u0151 gyerek a f\u0151n\u00f6k alatt. Biztos boldog ett\u0151l. :)<\/p>\n<p><strong>UPDATE!<\/p>\n<p>Babatologat\u00e1s k\u00f6zben r\u00e1j\u00f6ttem, hogy a fenti p\u00e9lda hib\u00e1s, elrontja a hierarchi\u00e1t, mert csak egy node-ot mozgatok, nem egy komplett r\u00e9szf\u00e1t, \u00edgy \u00e1rv\u00e1n maradnak J\u00f3zsi beosztottjai. A k\u00f6vetkez\u0151 r\u00e9szben bemutatom a jav\u00edt\u00e1st.<br \/>\n<\/strong><\/p>\n<p>Z\u00e1r\u00e1sul \u00e9rdekess\u00e9gk\u00e9nt n\u00e9zz\u00fck meg, hogy k\u00e9pezik a node azonos\u00edt\u00f3t, ha k\u00e9t eg\u00e9sz sz\u00e1m k\u00f6z\u00f6tt m\u00e1r nincs hely tov\u00e1bbi eg\u00e9sznek:<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nselect @jozsiujfonoke.GetDescendant(\r\n@jozsiujfonoke.GetDescendant(null, \r\n  @jozsiujfonoke.GetDescendant(null, null)).ToString(),\r\n  @jozsiujfonoke.GetDescendant(null, null).ToString()).ToString()\r\n<\/pre>\n<p>Kimenet:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n\/1\/0.1\/\r\n<\/pre>\n<p>Kicsit vad, de mi m\u00e1st lehetne kital\u00e1lni?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>P\u00e1r z\u00e1r\u00f3 gondolat a t\u00edpusr\u00f3l. Eddig mindig arra haszn\u00e1ltam, hogy egy hierarchi\u00e1ban a node-okhoz legyen k\u00f6zvetlen pointer\u00fcnk, ezzel egyes lek\u00e9rdez\u00e9seket jelent\u0151sen fel&#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,58],"tags":[],"class_list":["post-393","post","type-post","status-publish","format-standard","hentry","category-adatbazisok","category-sql-server-2008"],"_links":{"self":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/393","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=393"}],"version-history":[{"count":0,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/393\/revisions"}],"wp:attachment":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=393"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=393"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}