1

I'm working on a stored procedure to locate incorrectly formatted Addr2 lines and either auto-correct them, or move them to an exceptions table. I've been successful, to a degree-- the sproc 'skips' the way a record does, and has to be run two or three times before all rows are processed. This is obviously not what I want. After paring it down, I have come to the core issue:

----previous code to establish temp table #Addr2Chck, which inserts questionable order records and assigns row number------

set @NumberRecords = @@ROWCOUNT
set @rowcount=1

WHILE @rowcount<= @NumberRecords
BEGIN

SELECT @Cusid= Cusid,
@OrderNum= Ordernum,
@StndAddr2= Addr2,
from @Addr2Chck
where ROWID=@ROWCOUNT

update corp_orders
set addr2=@Stnd2
from (SELECT rowed, t.OrderNum from #Addr2Chck c left join corp_orders t   
on c.ordernum=t.ordernum) as j
where corp_orders.ordernum=@ordernum and corp_orders.cusid=@cusid and    j.rowid=@rowcount

SET @ROWCOUNT= @ROWCOUNT + 1
END

Obviously there are if-else statements that would take place within this loop, but if I don't get the simple version to work, I'm going to have surds all over the place. I'm sure I'm missing something glaringly obvious and have developed mind-blindness.

Here are some of the sources I've used for reference: http://www.java2s.com/Code/SQLServer/Transact-SQL/UsingROWCOUNT.htm Using row count from a temporary table in a while loop SQL Server 2008

as well as the Microsoft SQL AdventureWorks textbook.

I need to do this record by record instead of batching, which I'm sure is also a large part of the problem.

Thank you for your time!

ETA: This link was given to me at the beginning of the project as a reason why a cursor should be avoided: http://www.sqlbook.com/SQL/Avoiding-using-SQL-Cursors-20.aspx

Community
  • 1
  • 1
atomickiwi
  • 27
  • 5
  • why don't you just use a join instead of a while loop. sql is not about loops. – Hogan Oct 06 '15 at 21:56
  • @Hogan is correct, you should not really use any loops in SQL if you can help it. But if you really must then a CURSOR is what you should really use. – Paul Spain Oct 06 '15 at 23:12
  • Is the data being changed by someone else while your proc is running? You could try printing the @ROWCOUNT to see if that skips any rows. I expect its not skipping its count, but the data is now how changing under it. – Paul Spain Oct 06 '15 at 23:13
  • @Hogan-- unfortunately, a loop is what I've been told to use and, despite the fact various parts of this structure have been falling off each time I tweak it since August. No dice getting the CO to change his mind. I've also been told to avoid a cursors, with this link given for reasoning: http://www.sqlbook.com/SQL/Avoiding-using-SQL-Cursors-20.aspx – atomickiwi Oct 07 '15 at 00:08
  • I love the way that the article finishes up with "In this article we have seen how SQL Cursors can cause performance problems and affect other queries by locking tables". Um no, we've just seen an anonymous author allege those things. Personally, I would back Aaron Bertrand over an anonymous article https://sqlblog.org/2012/01/26/bad-habits-to-kick-thinking-a-while-loop-isnt-a-cursor. – DeanOC Oct 07 '15 at 04:37
  • 1
    @DeanOC-- I'm definitely not arguing in favor of the parameters I've been given-- I'm not experienced enough to have an opinion. But, when I was given that link, it was pretty much implied that extrapolating from it would be 'easy'. Two weeks later... – atomickiwi Oct 07 '15 at 10:49
  • @PaulSpain-- I'm working in our test environment right now, so the data isn't currently being changed. However, it will be extremely fluid and changeable in live. I will try printing the row count-- thanks for the suggestion! – atomickiwi Oct 07 '15 at 10:49

1 Answers1

0

First: full agreement with all the comments - loops in SQL are almost always avoidable. Lets ignore that and answer the question for now.

There were also typos in you code - I assume most are accidental. The one that concerns me most is that you have BOTH FROM @Addr2Chck and FROM #Addr2Chck. Both of these are possible incarnations of a temporary table. Using BOTH could be the source of your problem.

My answer assumes a TABLE VAR, something like

DECLARE @Addr2Chck TABLE (RowID INT PRIMARY KEY , CusID INT, OrderNum INT, Addr2 VARCHAR(99))

Yes, note the KEY on RowID. One possible cause of your error is that you are ending up with DUPLICATES on RowID earlier (in the code you omitted).

My suggestion: Do not use @@ROWCOUNT or independent incremented variable.

Try something like this variation. Still an evil loop, but it avoids the assumption that RowID is perfectly distributed across the row count.

DECLARE @RowIDNow INT
DECLARE @CusID INT, @OrderNum INT, @StndAddr2 VARCHAR(MAX)
-- test data
insert into @Addr2Chck values (1, 2,3,'norwalk')
insert into @Addr2Chck values (3, 4,5,'westchester')

SET NOCOUNT ON 
WHILE EXISTS (SELECT * FROM @Addr2Chck )  
BEGIN

SET @RowIDNow = (SELECT MIN( RowID ) FROM @Addr2Chck )
print CONVERT(VARCHAR(33),GETDATE(), 121) + ' # ' + STR(@RowIDNow) -- purely for illustration

SELECT @Cusid= Cusid,
   @OrderNum= Ordernum,
   @StndAddr2= Addr2
from @Addr2Chck
where ROWID=@RowIDNow

-- note: changed #Addr2Chck to @Addr2Chck
update corp_orders
   set addr2=@Stnd2
from (SELECT rowed, t.OrderNum from @Addr2Chck c left join corp_orders t   
   on c.ordernum=t.ordernum) as j
where corp_orders.ordernum=@ordernum and corp_orders.cusid=@cusid and       j.rowid= @RowIDNow

DELETE FROM @Addr2Chck WHERE RowID = @RowIDNow;
END -- of the evil look
Stan
  • 985
  • 1
  • 7
  • 12
  • 1
    Ah, sorry about the typos-- was altering the code a little to be as generic as possible. Thank you so much! Avoiding the assumption that the row count is evenly distributed is EXACTLY what I needed, without knowing I needed it. If nothing else, this whole exercise has illustrated a number of excellent reasons not to use loops. I really appreciate the assistance! – atomickiwi Oct 11 '15 at 13:57