I have inherited a legacy database which has a table with a composite primary key of varchar columns. One of the triggers on the table does not handle the case of having multiple rows in the inserted table. My attempted fix was to write a simple loop that would iterate through all rows and process them one at a time. In the past this technique has worked for me in cases where a set-based solution was not suitable, but I had only used it on tables with a primary key of a single column of type integer.
An index variable is initially set to -1 and a query within the loop retrieves the top one row with key greater than the index from the table sorted in ascending order. On each iteration the index is set to the key of the row retrieved. Once the last row has been retrieved, the query to retrieve a row with key greater than the index returns null which serves as the condition for exiting the loop.
I tried to adapt this technique to the legacy table with a composite varchar key. Unfortunately, the loop would never exit. Here is a minimal code sample that demonstrates the problem (all samples were run under SQL Server 2008):
-- Set up test data. DECLARE @test TABLE ( v1 varchar(10), v2 varchar(10) ) -- Values are assumed to conform to unique constraint on combinations of v1 and v2.
insert into @test (v1, v2) values ('rrrrrrrrrr', 'xxxxxxxxx') insert into @test (v1, v2) values ('rrrrrrrrrr', 'ddddddddd') insert into @test (v1, v2) values ('llllllllll', 'jjjjjjjjj') -- Iterate through all rows. DECLARE @v1 varchar(10) DECLARE @v2 varchar(10) SET @v1 = '' SET @v2 = '' WHILE (1 = 1) BEGIN SELECT TOP 1 @v1 = v1, @v2 = v2 FROM @test WHERE (v1 + v2) > (@v1 + @v2) ORDER BY (v1 + v2) ASC
-- Perform business logic here. print @v1 + '|' + @v2 IF @v1 Is null BREAK END-- Output from the above code is:
-- llllllllll|jjjjjjjjj
-- rrrrrrrrrr|ddddddddd
-- rrrrrrrrrr|xxxxxxxxx
-- rrrrrrrrrr|xxxxxxxxx
-- rrrrrrrrrr|xxxxxxxxx
-- . . . (last row repeats forever)
Next I tried to get it to work by setting each variable one at a time like this:
DECLARE @test TABLE ( v1 varchar(10), v2 varchar(10) ) insert into @test (v1, v2) values ('rrrrrrrrrr', 'xxxxxxxxx') insert into @test (v1, v2) values ('rrrrrrrrrr', 'ddddddddd') insert into @test (v1, v2) values ('llllllllll', 'jjjjjjjjj') DECLARE @v1 varchar(10) DECLARE @v2 varchar(10) SET @v1 = '' SET @v2 = '' WHILE (1 = 1) BEGIN SET @v1 = (SELECT top 1 v1 FROM @test WHERE (v1 + v2) > (@v1 + @v2) ORDER BY (v1 + v2) ASC) SET @v2 = (SELECT top 1 v2 FROM @test WHERE (v1 + v2) > (@v1 + @v2) ORDER BY (v1 + v2) ASC) print @v1 + '|' + @v2 IF @v1 Is null BREAK END
-- Output from the above code is:
-- llllllllll|jjjjjjjjj
-- rrrrrrrrrr|ddddddddd
This one exits but only after outputting two rows. The reason is that the setting of @v1 messes up the where-clause in the select-statement for @v2. Finally, I wrote this version with a temporary variable:
DECLARE @test TABLE ( v1 varchar(10), v2 varchar(10) ) insert into @test (v1, v2) values ('rrrrrrrrrr', 'xxxxxxxxx') insert into @test (v1, v2) values ('rrrrrrrrrr', 'ddddddddd') insert into @test (v1, v2) values ('llllllllll', 'jjjjjjjjj') DECLARE @v1 varchar(10) DECLARE @v2 varchar(10) DECLARE @temp varchar(10) SET @v1 = '' SET @v2 = '' WHILE (1 = 1) BEGIN SET @temp = (SELECT top 1 v1 FROM @test WHERE (v1 + v2) > (@v1 + @v2) ORDER BY (v1 + v2) ASC) SET @v2 = (SELECT top 1 v2 FROM @test WHERE (v1 + v2) > (@v1 + @v2) ORDER BY (v1 + v2) ASC) SET @v1 = @temp print @v1 + '|' + @v2 IF @v1 Is null BREAK END
-- Output from the above code is:
-- llllllllll|jjjjjjjjj
-- rrrrrrrrrr|ddddddddd
-- rrrrrrrrrr|xxxxxxxxx
I can use this last approach to deal with the three-part composite primary key of varchar columns in the legacy table but it sure is going to be ugly (the nature of the problem necessitates a certain degree of ugliness but I am trying to keep it in check).
- Why doesn't my first attempt work? What is it about setting the variables in one statement that prevents the query from working properly? Is it a bug?
- Is there some other technique that would be better for iterating through the rows one at a time?