﻿<?xml version='1.0' encoding='UTF-8'?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>SQLServerCentral / Article Discussions / Article Discussions by Author / Discuss content posted by Arshad Ali  / SQL Server 2008 T-SQL Enhancements Part - III / Latest Posts</title><generator>InstantForum.NET v2.9.0</generator><description>SQLServerCentral</description><link>http://www.sqlservercentral.com/Forums/</link><webMaster>notifications@sqlservercentral.com</webMaster><lastBuildDate>Wed, 22 May 2013 02:15:28 GMT</lastBuildDate><ttl>20</ttl><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>Trying to see how hierarchyId beats storing a "path"-style key, and it's not coming to me: a string of delimited (possibly tagged) local names, like a file path, can be searched using "LIKE" in ways that are much like XPath or LDAP queries. With prefix compression in indices, you don't pay a heavy price in Btree depth on that; and clustering works nicely, too. What am I missing, here?</description><pubDate>Mon, 29 Mar 2010 22:42:33 GMT</pubDate><dc:creator>mischa sandberg</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>Nice article thanks.  I like the compare and contrast between 2k5 and 2k8 that you demonstrated.</description><pubDate>Fri, 19 Mar 2010 11:56:05 GMT</pubDate><dc:creator>SQLRNNR</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>hi arshad,we are already using the same logic you explained for sql server 2005.we don't have sql server 2000, so we don't have to call a stored procedure recursively.we have several sorts of multilevel network marketing implementation.some companies which use similar business logic are amwa_ and for_v_rlivin_this can be viewed as layers or tiers.the whole hierarchy can be called trees, stars or simply networks.in one of our cases, we are limited to a table with columns parent, tier2, tier3, ..., tier10.i guess, i would need to rewrite ddl for this table with hierarchyid, but later this change will imply changing a lot sql logic already written. this can be done because this client has sql server 2008.it looks simple, but any tiers, including parent column, can have multiple rows, and of course some tiers are plain null, except by parent column.we have successfully drawn trees from these tables, for which the hardest case is this table with columns parent, tier2, tier3, ..., tier10.one of these trees is in a flex front-end, and the other is an asp.net treeview as gui.your article has given me plenty of insight about tree structure business logic, and i stopped by to say thank you.my guess is that hierachyid is in our short-term future for database programming.keep up your good work!!best regards,tonci korsano</description><pubDate>Fri, 19 Mar 2010 11:37:24 GMT</pubDate><dc:creator>Tonci Korsano</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>Good article :-)I used recursion in in 2000 and CTE in 2005, and always had the thought that while my solution seemed clever, the aftermath of dealing with it was always a pain. Moving nodes and children and/or getting all children direct and indirect or all ancestors direct or indirect etc. The implementation in 2008/R2, while not perfect has come a long way. I didn't realize it was implemented via CLR until reading the article, but that makes sense as it is calling methods and such.I rated the article 5 stars, but my one complaint is that you squeezed UDT's into the picture after an already full article. UDT's might not fit by themselves into an article, but I think the Hierarchy implementation was quite enough for this article. I know, picky, picky, but that just my 2 cents worth of constructive criticism.Regards,Toby</description><pubDate>Fri, 19 Mar 2010 08:08:45 GMT</pubDate><dc:creator>Toby White</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>I tend to roll-my-own when it comes to Hierarchies.Here's how I do it.Say I have a list of Employees with a Key on IDI then make a Employee_Hierarchy table then an Employee_SuperHierarchy tableEmployee_Hierarchy contains the parent and child Employee Primary Keys and is a compound unique index.Employee_SuperHierarchy contains the parent and child Employee Primary key and the Gap.For every Employee row there's an Row in with Gap=0It contains the expanded hierarchy so if (A) is the boss of (B) and (B) is the boss of (C) thenA, A, 0B, B, 0C, C, 0A, B, 1B, C, 1A, C, 2You can then loop until no more are inserted to build the full hierarchy by INSERT INTO Employee_SuperHierarchy(Parent, Child, Gap)SELECT Parent.ParentID, Child.ChildID, Child.Gap + 1FROM Employee_SuperHierarchy ChildINNER JOIN Employee_Hierarchy ParentON Child.ParentID = Parent.ChildIDThen you can easily run a query to find all the employees (or Bosses) of a person.  Do make sure to exclude Gap = 0 (employs self/Self boss)E.g. To find all the employeesSELECT Employee.*FROM Employee BossINNER JOIN Employee_SuperHierarchyON Employee_SuperHierarchy.ParentID = Boss.IDINNER JOIN EmployeeON Employee.ID = Employee_SuperHierarchy.ChildIDWHERE Boss.Name = 'Stalin'</description><pubDate>Fri, 19 Mar 2010 06:41:38 GMT</pubDate><dc:creator>Rob-1134588</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>@gerald.drouin,While loops do offer a solution for hierarchy based solution, but CTEs are simpler, cleaner way of solving the recursive problems. If correctly used, they offer better performance than while loop based solution.Relevant read can be [url] http://strictlysql.blogspot.com/2010/01/recursive-cte-vs-temp-table-performance.html [/url]</description><pubDate>Fri, 19 Mar 2010 01:29:46 GMT</pubDate><dc:creator>arr.nagaraj</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>after playing with hierarchyid and mainly it's GetReparentedValue feature i have to say I am disappointed.As well as the problem mentioned above by kent.kester I found that using GetReparentedValue to move  a record will [b]not[/b] also move the record's children, i thought that was the whole point, why would I want orphan records in my table?This certainly feels like a work in progress by Microsoft and hopefully SS2008 r2 will have some improvements.</description><pubDate>Wed, 17 Feb 2010 17:10:07 GMT</pubDate><dc:creator>paolosaurus</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>I really thought I'd love the use of the hierarchyid type... but things are slipping for me.. sorry if I am hijacking this a bit, but it's relevant.I am finding it quicker to do a flattened view using ParentId -&gt; Id than Node.GetAncestor(1) = NodeIs there a reason? :blink:I've got a million rows in a catalgoue table that looks like this:[code]CREATE TABLE [dbo].[tblHierarchy](  [Id] [int] IDENTITY(1,1) NOT NULL,  [ParentId] [int] NULL,  [Node] [hierarchyid] NOT NULL,  [ParentNode]  AS ([Node].[GetAncestor]((1))) PERSISTED,  [NodeLevel]  AS ([Node].[GetLevel]()) PERSISTED,  [Code] [nvarchar](64) NOT NULL,  [TypeId] [int] NOT NULL,  [TargetId] [int] NULL,  [CreateDate] [datetime] NOT NULL,  [CreateUser] [nvarchar](32) NOT NULL,  [UpdateDate] [datetime] NOT NULL,  [UpdateUser] [nvarchar](32) NOT NULL,  [UpdateCode] [nchar](1) NOT NULL,  CONSTRAINT [tblHierarchy_PK] PRIMARY KEY NONCLUSTERED   (    [Id] ASC  ) ON [PRIMARY],  CONSTRAINT [tblHierarchy_AK1] UNIQUE CLUSTERED   (    [Node] ASC  ) ON [PRIMARY]) ON [PRIMARY][/code]Simples... I've had lots of different indexes on, but I've stripped them off to get back to basics.The tree I am querying has 21000 records.If I do the following:[code]SELECT  [H1Id] = h1.Id  ,[H1Code] = H1.Code  ,[H1Type] = H1.TypeId  ,[H2Grp] = H2.Id  ,[H2Code] = H2.Code  ,[H2Type] = H2.TypeId  ,[H3Grp] = H3.Id  ,[H3Code] = H3.Code  ,[H3Type] = H3.TypeId  ,[H4Grp] = H4.Id  ,[H4Code] = H4.Code  ,[H4Type] = H4.TypeIdFROM dbo.tblHierarchy H0 WITH (NOLOCK)  INNER JOIN dbo.tblCATHierarchy H1 WITH (NOLOCK)    ON H0.Id = H1.ParentId  INNER JOIN dbo.tblCATHierarchy H2 WITH (NOLOCK)    ON H1.Id = H2.ParentId  INNER JOIN dbo.tblCATHierarchy H2 WITH (NOLOCK)    ON H2.Id = H3.ParentId  INNER JOIN dbo.tblCATHierarchy H4 WITH (NOLOCK)    ON H3.Id = H4.ParentIdWHERE H0.Code = 'ROOT'  AND H1.TypeId = 1[/code]Compared to:[code]SELECT  [H1Id] = h1.Id  ,[H1Code] = H1.Code  ,[H1Type] = H1.TypeId  ,[H2Grp] = H2.Id  ,[H2Code] = H2.Code  ,[H2Type] = H2.TypeId  ,[H3Grp] = H3.Id  ,[H3Code] = H3.Code  ,[H3Type] = H3.TypeId  ,[H4Grp] = H4.Id  ,[H4Code] = H4.Code  ,[H4Type] = H4.TypeIdFROM dbo.tblHierarchy H0 WITH (NOLOCK)  INNER JOIN dbo.tblCATHierarchy H1 WITH (NOLOCK)    ON H0.Node = H1.Node.GetAncestor(1)  INNER JOIN dbo.tblCATHierarchy H2 WITH (NOLOCK)    ON H1.Node = H2.Node.GetAncestor(1)  INNER JOIN dbo.tblCATHierarchy H2 WITH (NOLOCK)    ON H2.Node = H3.Node.GetAncestor(1)  INNER JOIN dbo.tblCATHierarchy H4 WITH (NOLOCK)    ON H3.Node = H4.Node.GetAncestor(1)WHERE H0.Code = 'ROOT'  AND H1.TypeId = 1[/code]The first query runs shed twice as fast. Even if I substitute Node.GetAncestor(1) with my persisted computed column of ParentNode.It's important for the dataset to be flattened for exporting to other systems. If someone reading this has better plans please reply or PM me. would be grateful as always.I guess we may need both approaches?  It's incredibly powerful, but it has it's limits?Kev</description><pubDate>Tue, 01 Sep 2009 10:14:02 GMT</pubDate><dc:creator>Fozzie</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>Thanks Carla,Yeah I missed to include declare statement, sorry for inconvenience.Reader,Kindly add these declare statement in script existing in first table of the article.[font="Courier New"]DECLARE @CTO INTDECLARE @DevManager INTDECLARE @TESTManager INTDECLARE @DevLead1 INTDECLARE @DevLead2 INT	DECLARE @TESTLead INT[/font]</description><pubDate>Thu, 27 Aug 2009 10:58:40 GMT</pubDate><dc:creator>Arshad Ali-556241</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>I wasn't aware of this addition to SQL 2008 until I read your article.  It's interesting but somewhat kludgey.I played around with hierarchyid after reading this article and discovered it can be surprising easy to mess up records.  Specifically, the GetReparentedValue doesn't seem to be very useful if you are moving children from one parent to another parent that already has children.  It will actually assign duplicate hierarchyid's.Here's an example of it happening.[code]USE [Test]GO/****** Object:  Table [dbo].[hier_test1]    Script Date: 08/27/2009 11:14:30 ******/IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[hier_test1]') AND type in (N'U'))DROP TABLE [dbo].[hier_test1]GOUSE [Test]GO/****** Object:  Table [dbo].[hier_test1]    Script Date: 08/27/2009 11:14:30 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [dbo].[hier_test1](	[record_id] [int] IDENTITY(1,1) NOT NULL,	[data1] [nvarchar](50) NULL,	[data2] [nvarchar](2000) NULL,	[hier_id] [hierarchyid] NOT NULL,	[position] [nvarchar](50) NULL, CONSTRAINT [PK_hier_test1] PRIMARY KEY CLUSTERED (	[record_id] ASC)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]) ON [PRIMARY]GOinsert into Test.dbo.hier_test1(	data1,	data2,	hier_id)VALUES(	'/',	'This is the root record',	hierarchyid::GetRoot())exec AddHierRecord 1, '/1', 'Child of Root'exec AddHierRecord 1, '/2', 'Child of Root'exec AddHierRecord 1, '/3', 'Child of Root'exec AddHierRecord 2, '/1/1', 'Child of Child 1'exec AddHierRecord 2, '/1/2', 'Child of Child 1'exec AddHierRecord 2, '/1/3', 'Child of Child 1'exec AddHierRecord 3, '/2/1', 'Child of Child 2'exec AddHierRecord 3, '/2/2', 'Child of Child 2'exec AddHierRecord 3, '/2/3', 'Child of Child 2'exec AddHierRecord 4, '/3/1', 'Child of Child 3'exec AddHierRecord 4, '/3/2', 'Child of Child 3'exec AddHierRecord 4, '/3/3', 'Child of Child 3'exec AddHierRecord 5, '/1/1/1', 'Child of Child 1 of Child 1'exec AddHierRecord 5, '/1/1/2', 'Child of Child 1 of Child 1'exec AddHierRecord 5, '/1/1/3', 'Child of Child 1 of Child 1'exec AddHierRecord 6, '/1/2/1', 'Child of Child 2 of Child 1'exec AddHierRecord 6, '/1/2/2', 'Child of Child 2 of Child 1'exec AddHierRecord 6, '/1/2/3', 'Child of Child 2 of Child 1'select *, hier_id.ToString()from	hier_test1declare @oldparent hierarchyiddeclare @newparent hierarchyidselect @oldparent = hier_idfrom	hier_test1where	record_id = 2select @newparent = hier_idfrom	hier_test1where	record_id = 3update hier_test1set		hier_id = hier_id.GetReparentedValue(@oldparent, @newparent)where	hier_id.IsDescendantOf(@oldparent) = 1	and record_id &lt;&gt; 2select *, hier_id.ToString()from	hier_test1[/code][img]http://www.houseofelder.com/images/hierarchyproblem.jpg[/img]Look at record_id's 5 and 8 in the second set of results.  Then there's the issue of the "grandchildren" records.  It does attempt to move these.  When it does, however, it becomes impossible to distinguish which of two parents they belong to.  I've gone through the tutorials and documentation on TechNet and they don't offer any examples of moving a branch into an already populated branch.If anyone can tell me I'm doing something wrong I'd really like to know what it is so I can make use of this new feature.</description><pubDate>Thu, 27 Aug 2009 09:49:06 GMT</pubDate><dc:creator>LerxtDBA</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>Arshad Ali,Thank you for an excellent, well-written article!  (Note: the code to populate your sample table is missing the declares for @CTO, etc.)gerald.drouin,Clever solution!</description><pubDate>Thu, 27 Aug 2009 08:56:10 GMT</pubDate><dc:creator>Carla Wilson-484785</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>Thanks for the article, makes me look forward to our upcoming migration from 2000 to 2008.However, I have to ask why you would actually try to use recursion for the 2000 example?  Especially with the well-known issues around nesting, why provide such a solution?  When I need to do hierarchies, I use something more like this:[code="sql"]create procedure EmployeeHierarchy @EmpID intasdeclare @out table (EmpID int, EmpName varchar(255), ReportsTo int)insert into @outselect	EmpID, EmpName, ReportsTofrom	Employees_2000_2005where	EmpID = @EmpIDwhile @@rowcount &gt; 0	insert into @out	select	EmpID, EmpName, ReportsTo	from	Employees_2000_2005	where	ReportsTo in (select EmpID from @out)		and EmpID not in (select EmpID from @out)select o.EmpID, o.EmpName, e.EmpName Manager from 		@out oinner join	Employees_2000_2005 e	on	o.ReportsTo = e.EmpIDorder by 1go[/code]A routine like this self-limits, so no infinite loops in cases when somebody messes up the list.  No problems with recursion levels either.  If the @out table gets too big, convert it to a temp table with indexes.  Or am I just missing something?Again, thanks for the article!</description><pubDate>Thu, 27 Aug 2009 07:24:24 GMT</pubDate><dc:creator>gerald.drouin</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>Great article! Thanks!Little hint for .NET developers:New .NET wrapper types SqlHierarcyId, SqlGeography, SqlGeometry are not stored in System.Data.dll but in "Microsoft.SqlServer.Types.dll" in "c:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\" directory. After adding the reference to your project you can find them in namespace "Microsoft.SqlServer.Types".GreetsFlo</description><pubDate>Thu, 27 Aug 2009 07:17:41 GMT</pubDate><dc:creator>Florian Reischl</dc:creator></item><item><title>RE: SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>great article....thank you very much.....the simplicity of the language is greatly appreciated...do come up with more of such articles...thank you</description><pubDate>Thu, 27 Aug 2009 04:17:14 GMT</pubDate><dc:creator>sanjeet.uchil</dc:creator></item><item><title>SQL Server 2008 T-SQL Enhancements Part - III</title><link>http://www.sqlservercentral.com/Forums/Topic778038-1463-1.aspx</link><description>Comments posted to this topic are about the item [B]&lt;A HREF="/articles/SQL+Server+2008/67787/"&gt;SQL Server 2008 T-SQL Enhancements Part - III&lt;/A&gt;[/B]</description><pubDate>Wed, 26 Aug 2009 22:47:16 GMT</pubDate><dc:creator>Arshad Ali-556241</dc:creator></item></channel></rss>