<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ijonas.com &#187; code samples</title>
	<atom:link href="http://ijonas.com/tag/code-samples/feed/" rel="self" type="application/rss+xml" />
	<link>http://ijonas.com</link>
	<description>Habitual Ramblings of an Indiginous Ijonous</description>
	<lastBuildDate>Mon, 02 Jan 2012 15:12:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Storing URIs in SQL Server 2008 using the HIERARCHYID datatype and LINQ-To-SQL</title>
		<link>http://ijonas.com/software-development/96/</link>
		<comments>http://ijonas.com/software-development/96/#comments</comments>
		<pubDate>Wed, 14 May 2008 14:47:41 +0000</pubDate>
		<dc:creator>ijonas</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[code samples]]></category>
		<category><![CDATA[HierarchyId]]></category>
		<category><![CDATA[HOWTO]]></category>
		<category><![CDATA[LINQ]]></category>
		<category><![CDATA[LINQ-To-SQL]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://ijonas.wordpress.com/?p=96</guid>
		<description><![CDATA[I&#8217;ve been playing around with SQL Server 2008 CTP, exploring the benefits of the new HIERARCHYID datatype, which has been designed to efficiently store depth-first tree structures in SQL Server 2008. My requirements were to store URI paths such as ones &#8230; <a href="http://ijonas.com/software-development/96/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been playing around with SQL Server 2008 CTP, exploring the benefits of the new HIERARCHYID datatype, which has been designed to efficiently store depth-first tree structures in SQL Server 2008.</p>
<p>My requirements were to store URI paths such as ones you might find in URLs, whereby each path component is held as a separate record in a table. Each path component refers to a folder in file system, e.g.</p>
<pre><strong>URL:</strong> http://www.vamosa.com/index/information_solutions/technology_and_products/vamosa_content_migrator.htm
<strong>URI:</strong> path: /index/information_solutions/technology_and_products/vamosa_content_migrator.htm</pre>
<p>So traditionally in the database I would end up with parent/child relationship as such</p>
<pre><strong>id 	label					parent_id
</strong>0	NULL					NULL		&lt;--- root element
1	index					0
2	information_solutions			1
3	technology_and_products			2
4	vamosa_content_migration.htm	<span style="white-space:pre;">	</span>3</pre>
<p>HIERARCHYID allows you to perform all sorts of fast hiearchical queries without having to wander up and down the parent/child foreign key relationship. Those are the benefits, however there&#8217;s a problem&#8230;</p>
<p>At the time of writing the .NET Framework 3.5&#8242;s System.Data.SqlTypes namespace does not provide any support for HIERARCHID datatypes, e.g. SqlHierarchId as a class is missing, meaning that if you want to work with the new type you&#8217;ll need hide it from your .NET code using stored procedures, and that&#8217;s the approach I&#8217;ve taken in this post.</p>
<p>The other thing to be aware of is that the HIERARCHYID column is not a foreign key to a record further up the tree. The HIERARCHYID column stores position of the current record within the tree, e.g.</p>
<pre><strong>id  </strong><span style="white-space:pre;"><strong>	</strong></span><strong>label					hierarchyid_field.ToString()
</strong>0	NULL					/	<span style="white-space:pre;">	</span>&lt;--- root element
1	index					/1/
2	information_solutions			/1/1/
3	technology_and_products			/1/1/1/
4	vamosa_content_migrator.htm		/1/1/1/1/</pre>
<p>The table I&#8217;ve created is called URI and is defined as follows:</p>
<pre lang="sql" line="1">CREATE TABLE dbo.URI
(
	Id uniqueidentifier NOT NULL,
	Label nvarchar(256) NULL,
	UriHID hierarchyid NOT NULL,           -- the magical new datatype
	CONSTRAINT [PK_URI] PRIMARY KEY
)
</pre>
<p>I want to be able call my stored proc as follows:</p>
<pre lang="java" line="1">
  MyLINQDataContext db = new MyLINQDataContext(); // a previously defined context via .dbml file
  System.Guid? uri_id=null;			  // variable to capture the URI.Id of the last node inserted
  db.InsertURI("/products/vcm/index.html", ref uri_id);	// call to stored proc
  Console.Out.Writeline( uri_id );		 // doing something useful with the return result
</pre>
<p>Stored procedures aren&#8217;t my strongest point by a long stretch so feel free to comment and improve the code, but here&#8217;s the InsertURI stored proc:</p>
<pre lang="sql" line="1">CREATE Procedure InsertURI
	@uri nvarchar(4000),
	@uri_id uniqueidentifier OUTPUT
AS
	declare @root_uri_id uniqueidentifier
	declare @path_remainder nvarchar(4000)
	declare @pos_slash int
	declare @parent_uri_hid HIERARCHYID

	set @parent_uri_hid = HIERARCHYID::GetRoot()

	-- ensure the root exists
	select @root_uri_id = u.id from URI u where u.label is null
	if @root_uri_id is null
		insert into URI values (NEWID(), NULL, @parent_uri_hid)

	set @path_remainder = @uri
	set @pos_slash = charindex( '/', @path_remainder )
	while (len(@path_remainder) > 0)
	begin
		declare @next_slash int
		declare @uri_label nvarchar(256)
		declare @current_uri_hid HIERARCHYID
		set @next_slash = charindex( '/', @path_remainder, @pos_slash+1 )

		-- determine the next label in the sequence of depth-first node names
		if @next_slash > 0
		begin
			set @uri_label = SUBSTRING( @path_remainder, @pos_slash+1, @next_slash - @pos_slash - 1 )
			set @path_remainder = substring( @path_remainder, @next_slash, len(@path_remainder) )
		end
		else
		begin
			set @uri_label = SUBSTRING( @path_remainder, @pos_slash+1, len(@path_remainder) )
			set @path_remainder = ''
		end
		set @pos_slash = charindex( '/', @path_remainder )
		-- determine if current @uri_label exists as child of @parent_uri_hid
		set @current_uri_hid = NULL
		select @current_uri_hid = u.UriHID from URI u where u.UriHID.GetAncestor(1) = @parent_uri_hid and u.Label=@uri_label
		if @current_uri_hid is null
		begin
			-- the label doesn't exist as a child, hence create it
			-- first determine the new hierarchyid - new node will be last in row of siblings
			declare @last_child_uri_hid HIERARCHYID
			SELECT @last_child_uri_hid = MAX(UriHID) FROM URI u WHERE u.UriHID.GetAncestor(1) = @parent_uri_hid
			set @current_uri_hid = @parent_uri_hid.GetDescendant(@last_child_uri_hid, NULL)

			set @uri_id = NEWID()
			insert into uri values (@uri_id, @uri_label, @current_uri_hid )
		end
		set @parent_uri_hid = @current_uri_hid
	end</pre>
<p>The stored proc takes the @uri string and chops it using the &#8216;/&#8217;-separator (orange highlighted), looping through each element in the path (red brown highlighted), first &#8216;index&#8217; then &#8216;information_solutions&#8217;, etc. For each element it will check wether or not the element exists (red highlighted code), creating it if necessary (purple highlighted). The position of the new node will always be on the &#8216;far right&#8217; of its siblings (green highlighted code).<br />
 </p>
<p> </p>
<div style="padding-top:5px;padding-right:0px;padding-bottom:5px;padding-left:0px;;">
											<iframe
												style="height:25px !important; border:0px solid gray !important; overflow:hidden !important; width:460px !important;" frameborder="0" scrolling="no" allowTransparency="true"
												src="http://www.linksalpha.com/social?blog=ijonas.com&link=http%3A%2F%2Fijonas.com%2Fsoftware-development%2F96%2F&title=Storing+URIs+in+SQL+Server+2008+using+the+HIERARCHYID+datatype+and+LINQ-To-SQL&desc=I%27ve+been+playing+around+with+SQL+Server+2008+CTP%2C+exploring+the+benefits+of+the+new+HIERARCHYID%C2%A0datatype%2C+which+has+been+designed+to+efficiently+store+depth-first+tree+structures+in+SQL+Server+2008.&fc=333333&fs=arial&fblname=like&fblref=facebook&fbllang=en_US&fblshow=1&fbsbutton=0&fbsctr=1&fbslang=en&fbsendbutton=0&twbutton=1&twlang=en&twmention=ijonas&twrelated1=&twrelated2=&twctr=1&lnkdshow=noshow&lnkdctr=1&buzzbutton=0&buzzlang=en&buzzctr=1&diggbutton=1&diggctr=1&stblbutton=1&stblctr=1&g1button=1&g1ctr=1&g1lang=en-US">
											</iframe>
										</div>]]></content:encoded>
			<wfw:commentRss>http://ijonas.com/software-development/96/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

