| Author |
Topic |
|
Kristen
Test
22859 Posts |
Posted - 2010-06-03 : 10:46:11
|
I need to update a table to indicate which rows are the First / Last branch at that level.Setting the First seems to be easy - if previous row is at a lower level then the current row is the first.I'm struggling with Last, particularly when I get to the bottom of the tree and there are no further child branches to referenceIn the table, below, there are two data sets - and a DELETE statement which will leave one, or other, for testing.I've put First / Last on the data to show what it should be, and there are IsFirst / IsLast columns to be setThis will be called frequently, so needs to be as efficient as possible. Its doing my head in trying to work out how to do it!-- http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=145286SET NOCOUNT ONDECLARE @tblTree TABLE( [Scenario] int NOT NULL, [Sequence] int NOT NULL, [Level] int NOT NULL, [Value] varchar(10) NOT NULL, [First] varchar(10) NULL, [Last] varchar(10) NULL, [IsFirst] int NULL, [IsLast] int NULL, PRIMARY KEY ( [Scenario], [Sequence] ))INSERT INTO @tblTree( [Scenario], [Sequence], [Level], [Value], [First], [Last])SELECT *FROM(SELECT [Scenario] = 1, [Sequence] = 1, [Level] = 1, [Value] = '1.1', [First] = 'First', [Last] = '' UNION ALLSELECT 1, 2, 2, '2.1', 'First', '' UNION ALLSELECT 1, 3, 3, '3.1', 'First', '' UNION ALLSELECT 1, 4, 3, '3.2', '', '' UNION ALLSELECT 1, 5, 4, '4.1', 'First', '' UNION ALLSELECT 1, 6, 4, '4.2', '', 'Last' UNION ALLSELECT 1, 7, 3, '3.3', '', 'Last' UNION ALLSELECT 1, 8, 4, '4.4', 'First', 'Last' UNION ALLSELECT 1, 9, 2, '2.2', '', '' UNION ALLSELECT 1, 10, 2, '2.3' , '', 'Last' UNION ALLSELECT 1, 11, 1, '1.2' , '', 'Last' UNION ALL--SELECT 2, 1, 1, '1.1', 'First', '' UNION ALLSELECT 2, 2, 2, '2.1', 'First', '' UNION ALLSELECT 2, 3, 3, '3.1', 'First', '' UNION ALLSELECT 2, 4, 3, '3.2', '', '' UNION ALLSELECT 2, 5, 4, '4.1', 'First', '' UNION ALLSELECT 2, 6, 4, '4.2', '', 'Last' UNION ALLSELECT 2, 7, 3, '3.3', '', 'Last' UNION ALLSELECT 2, 8, 4, '4.4', 'First', 'Last' UNION ALLSELECT 2, 9, 2, '2.2', '', '' UNION ALLSELECT 2, 10, 2, '2.3' , '', 'Last' UNION ALLSELECT 2, 11, 3, '3.3' , 'First', 'Last' UNION ALLSELECT 2, 12, 4, '4.5' , 'First', 'Last' UNION ALLSELECT 2, 13, 5, '5.1' , 'First', 'Last' UNION ALLSELECT 2, 14, 1, '1.2' , '', 'Last') AS TORDER BY [Sequence]DELETE DFROM @tblTree AS DWHERE [Scenario] <> 1 -- Set to Scenario you want to test-- This seems to work for "first"UPDATE TSET [IsFirst] = 1FROM @tblTree AS T LEFT OUTER JOIN @tblTree AS T_Prev ON T_Prev.[Sequence] = T.[Sequence] - 1WHERE T_Prev.[Sequence] IS NULL -- First row OR T_Prev.[Level] < T.[Level]SELECT [IsFirst], [IsLast], [First], [Last], [Level], REPLICATE('..', [Level]-1) + [Value]FROM @tblTreeORDER BY [Sequence]SET NOCOUNT OFF |
|
|
Kristen
Test
22859 Posts |
Posted - 2010-06-03 : 11:04:21
|
I wonder if this is good enough?UPDATE TSET [IsLast] = 1FROM @tblTree AS T LEFT OUTER JOIN @tblTree AS T_Next ON T_Next.[Sequence] = T.[Sequence] + 1WHERE T_Next.[Sequence] IS NULL -- Last row OR T_Next.[Level] < T.[Level] OR NOT EXISTS ( SELECT * FROM @tblTree AS T1 WHERE T1.[Sequence] > T.[Sequence] -- Following items AND T1.[Level] = T.[Level] -- Same level ) |
 |
|
|
lazerath
Constraint Violating Yak Guru
343 Posts |
Posted - 2010-06-03 : 20:08:16
|
I'm a sucker for hierarchies, and I haven't seen this approach before, so I pretty much had to find an elegant, over thought approach. :-)------------------------------------------------------- Your initialization code-----------------------------------------------------SET NOCOUNT ONDECLARE @tblTree TABLE( [Scenario] int NOT NULL, [Sequence] int NOT NULL, [Level] int NOT NULL, [Value] varchar(10) NOT NULL, [First] varchar(10) NULL, [Last] varchar(10) NULL, [IsFirst] int NULL, [IsLast] int NULL PRIMARY KEY ( [Scenario], [Sequence] ))INSERT INTO @tblTree( [Scenario], [Sequence], [Level], [Value], [First], [Last])SELECT *FROM(SELECT [Scenario] = 1, [Sequence] = 1, [Level] = 1, [Value] = '1.1', [First] = 'First', [Last] = '' UNION ALLSELECT 1, 2, 2, '2.1', 'First', '' UNION ALLSELECT 1, 3, 3, '3.1', 'First', '' UNION ALLSELECT 1, 4, 3, '3.2', '', '' UNION ALLSELECT 1, 5, 4, '4.1', 'First', '' UNION ALLSELECT 1, 6, 4, '4.2', '', 'Last' UNION ALLSELECT 1, 7, 3, '3.3', '', 'Last' UNION ALLSELECT 1, 8, 4, '4.4', 'First', 'Last' UNION ALLSELECT 1, 9, 2, '2.2', '', '' UNION ALLSELECT 1, 10, 2, '2.3' , '', 'Last' UNION ALLSELECT 1, 11, 1, '1.2' , '', 'Last' UNION ALL--SELECT 2, 1, 1, '1.1', 'First', '' UNION ALLSELECT 2, 2, 2, '2.1', 'First', '' UNION ALLSELECT 2, 3, 3, '3.1', 'First', '' UNION ALLSELECT 2, 4, 3, '3.2', '', '' UNION ALLSELECT 2, 5, 4, '4.1', 'First', '' UNION ALLSELECT 2, 6, 4, '4.2', '', 'Last' UNION ALLSELECT 2, 7, 3, '3.3', '', 'Last' UNION ALLSELECT 2, 8, 4, '4.4', 'First', 'Last' UNION ALLSELECT 2, 9, 2, '2.2', '', '' UNION ALLSELECT 2, 10, 2, '2.3' , '', 'Last' UNION ALLSELECT 2, 11, 3, '3.3' , 'First', 'Last' UNION ALLSELECT 2, 12, 4, '4.5' , 'First', 'Last' UNION ALLSELECT 2, 13, 5, '5.1' , 'First', 'Last' UNION ALLSELECT 2, 14, 1, '1.2' , '', 'Last') AS TORDER BY Scenario,[Sequence];------------------------------------------------------- Hierarchy Code-----------------------------------------------------WITH cte1AS(SELECT ROW_NUMBER() OVER(ORDER BY t1.Scenario, t1.Sequence) AS RowPK, t1.Scenario, t1.Sequence, t1.Level, MIN(t2.Sequence) AS NextSequence, t1.Value, t1.First, t1.Last, t1.IsFirst, t1.IsLastFROM @tblTreeAS t1LEFT JOIN @tblTreeAS t2ON t1.Scenario= t2.ScenarioAND t1.Sequence< t2.SequenceAND t1.Level>= t2.LevelGROUP BY t1.Scenario, t1.Sequence, t1.Level, t1.Value, t1.First, t1.Last, t1.IsFirst, t1.IsLast), cteTraverseAS(SELECT t1.RowPK, t1.Scenario, t1.Sequence, t1.Level, t1.NextSequence, t1.Value, CONVERT(BIGINT,NULL) AS ParentPK, t1.First, t1.LastFROM cte1AS t1WHERE t1.Level= 1UNION ALLSELECT t1.RowPK, t1.Scenario, t1.Sequence, t1.Level, t1.NextSequence, t1.Value, t2.RowPK AS ParentPK, t1.First, t1.LastFROM cte1AS t1JOIN cteTraverseAS t2ON t1.Scenario= t2.ScenarioAND t1.Sequence> t2.SequenceAND t1.Sequence< COALESCE(t2.NextSequence,t1.Sequence+1)AND t1.Level= t2.Level+1), cteTestAS(SELECT *, CASE ROW_NUMBER() OVER(PARTITION BY t1.Scenario, t1.ParentPK ORDER BY t1.Sequence ASC ) WHEN 1 THEN 1 END AS isFirst, CASE ROW_NUMBER() OVER(PARTITION BY t1.Scenario, t1.ParentPK ORDER BY t1.Sequence DESC) WHEN 1 THEN 1 END AS isLastFROM cteTraverseAS t1)SELECT *FROM cteTestAS t/*WHERE NOT ( t.First= CASE t.isFirst WHEN 1 THEN 'First' ELSE '' ENDAND t.Last= CASE t.isLast WHEN 1 THEN 'Last' ELSE '' END )--*/ORDER BY t.Scenario, t.Sequence;SET NOCOUNT OFF You don't need cteTest, just the part of it that does isFirst and isLast. With cteTest, you can just uncomment the where clause at the end to see that it works.I haven't performance tested your code, but I'd hazard a guess that this would outperform what you have on large datasets (with proper indexes). I could be wrong, so if you test it let me know. |
 |
|
|
Kristen
Test
22859 Posts |
Posted - 2010-06-04 : 02:22:01
|
| "I pretty much had to find an elegant, over thought approach"Hehehe ... thanks Lazerath, I'm out of the office for a couple of days but will try it next week. |
 |
|
|
|
|
|