2010-02-02 5 views
3

Si j'ai la table test avec deux colonnes num1 et NUM2 et le déclenchement suivant sur ce qui incrémente juste num2 sur des inserts de num1:SQL Server - Réécriture déclencheur pour éviter approche basée sur le curseur

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 

Existe-t-il un moyen de réécrire ce qui précède en utilisant une approche basée sur un ensemble?

(Au cas où quelqu'un veut savoir pourquoi je fais cela, voici l'arrière-plan: SQL Server A trigger to work on multiple row inserts)

Solution sans table temporaire à l'aide ROW_NUMBER (Sql partir de 2005 seulement):

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

Les déclencheurs sont les jouets du diable. .: D –

Répondre

0
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: Je n'ai pas pu tester cela, il pourrait y avoir des fautes de frappe.

Aussi, je suis sûr qu'il y a une solution de table non temporaire utilisant ROWNUMBER, mais je suis trop paresseux pour aller chercher la syntaxe. Mais vous pouvez l'utiliser comme un guide pour arriver à cette réponse, au lieu d'utiliser la table temporaire pour faire les nombres de 1 à N utiliser ROWNUMBER et l'ajouter au décalage (@ maxnum2)

+0

Cela fonctionne bien mais le "DEFINE @tmp" devrait être "DECLARE @tmp Table" et la jointure interne devrait utiliser un alias: "INNER JOIN @tmp T ON T .pInsNum = TEST.num1 ". – pug

+0

@pug: FYI vous pouvez également faire [@tmp] bien que je sois d'accord qu'un alias soit meilleur. – ErikE

1

juste une idée:

commencent tran pour éviter des changements pour tester

@max à déclarer t

select @max = max (num2) à partir essai

créer une table temporaire avec num1 et un indice d'auto-incrémentation (ex: idx (à partir de 1)

insérer votre insérée dans la table temporaire

insert en test (num1, num2) select num1, IDX + @ max de tmp

fin tran

+0

Drôle, je n'ai pas vu ta réponse quand j'ai écrit la mienne. Eh bien j'ai codé cet algorithme (sauf trans) dans ma réponse. Une question @munissor est-ce qu'il y a une chance de blocage si vous avez une transaction dans un trigger? – Hogan

+0

Oui, il y a possibilité de deadlocks, le trigger est comme n'importe quelle autre requête – munissor

1

Si je comprends bien, une normale la mise à jour vous obtiendrait ce que vous voulez.

UPDATE TEST 
SET  num2 = @NEWNUM2 
FROM TEST t 
     INNER JOIN Inserted i ON i.num1 = t.num1 
Questions connexes