views:

246

answers:

3

If I have table Test with two columns num1 and num2 and the following trigger on it which just increments num2 on inserts of num1:

 DECLARE @PROC_NEWNUM1 VARCHAR (10)
 DECLARE @NEWNUM2 numeric(20)
 DECLARE my_Cursor CURSOR FOR SELECT num1 FROM INSERTED;

 OPEN my_Cursor 
 FETCH NEXT FROM my_Cursor into @PROC_NEWNUM1

 WHILE @@FETCH_STATUS = 0 
 BEGIN 

 select @NEWNUM2 = MAX(num2) from TEST
 if @NEWNUM2 is null
 Begin
    set  @NEWNUM2  = 0
 End
 set @NEWNUM2 = @NEWNUM2 + 1
 UPDATE TEST SET num2 = @NEWNUM2  WHERE num1 = @PROC_NEWNUM1
 FETCH NEXT FROM my_Cursor into @PROC_NEWNUM1  
 END

CLOSE my_Cursor
DEALLOCATE my_Cursor

Is there a way to rewrite the above using a set based approach?

(In case anyone wants to know why I am doing this, here is the background: http://stackoverflow.com/questions/2178889/sql-server-a-trigger-to-work-on-multiple-row-inserts)

Solution without temp table using Row_Number (Sql 2005 onwards only):

SELECT @MAXNUM2 = MAX(num2) FROM TEST
if @MAXNUM2 IS NULL
BEGIN
    SET @MAXNUM2=0
END

UPDATE TEST
SET num2 = @MAXNUM2 + SubQuery.R
FROM
(
SELECT num1, ROW_NUMBER() OVER (ORDER BY num1) as R FROM inserted
)
SubQuery
INNER JOIN TEST on SubQuery.num1 =  TEST.num1
+1  A: 

Just an idea:

begin tran to avoid changes to test

declare @max int

select @max = max(num2) from test

create a temporary table with num1 and an autoincrement index (ex:idx (starting from 1)

insert your INSERTED into temporary table

insert into test(num1, num2) select num1, idx+@max from tmp

end tran

munissor
Funny, I did not see your answer when I wrote mine. Well I coded this algorithm (except for trans) in my answer. One question @munissor is there any chance of a deadlock if you have a transaction in a trigger?
Hogan
Yes, there is possibilitiy for deadlocks, trigger are like any other query
munissor
+1  A: 

If I understand correctly, a normal update would get you what you want.

UPDATE  TEST
SET     num2 = @NEWNUM2
FROM    TEST t
        INNER JOIN Inserted i ON i.num1 = t.num1
Lieven
+1  A: 
DECLARE @MAXNUM2 numeric(20)

-- First make an auto increment table starting at 1
DEFINE @tmp 
( 
   aNum int identity(1,1),
   pInsNum varchar(10)
)

INSERT INTO @tmp (pInsNum)
  SELECT num1 FROM INSERTED;

-- Now find offset
SELECT @MAXNUM2 = MAX(num2) FROM TEST

IF @MAXNUM2 is null
BEGIN
 SET @MAXNUM2  = 0
END

-- Do update
UPDATE TEST
SET num2 = @MAXNUM2 + aNum
FROM TEST 
   INNER JOIN @tmp  ON @tmp.pInsNum = TEST.num1

Note: I was not able to test this, there might be typos.

Also, I'm sure there is a non-temp table solution using ROWNUMBER, but I am too lazy to go look up the syntax. But you can use this as a guide to get to that answer, instead of using the temp table to make the numbers from 1 to N use ROWNUMBER and add that to the offset (@maxnum2)

Hogan
This works fine but the "DEFINE @tmp" should be "DECLARE @tmp Table" and inner join should use an alias: "INNER JOIN @tmp T ON T.pInsNum = TEST.num1".
pug
@pug: FYI you can also do [@tmp] though I agree an alias is better.
Emtucifor