2010-02-01 17 views
7

Estoy manteniendo un código que tiene un disparador en una tabla para incrementar una columna. Esa columna luego es utilizada por una aplicación de terceros A. Digamos que la tabla se llama prueba con dos columnas num1 y num2. El disparador se ejecuta en cada inserción de num1 en prueba. A continuación se presenta el gatillo:SQL Server Un disparador para trabajar en varias inserciones de fila

USE [db1] 
GO 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
ALTER TRIGGER [dbo].[TEST_MYTRIG] ON [dbo].[test] 
FOR INSERT AS 
begin 
SET NOCOUNT ON 
DECLARE @PROC_NEWNUM1 VARCHAR (10) 
DECLARE @NEWNUM2 numeric(20) 

SELECT @PROC_NEWNUM1 = num1 FROM INSERTED 
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 
SET NOCOUNT OFF 
End 

Esto funciona bien en simples inserciones basado fila, pero hay otra aplicación de 3 ª parte B (suspiro) que hace a veces múltiples insertos en esta mesa algo similar pero no exactamente:

INSERT INTO [db1].[dbo].[test] 
      ([num1]) 

    Select db1.dbo.test.num1 from [db1].[dbo].[test] 
GO 

Esto hace que el gatillo para comportarse de forma errática ...

Ahora bien, no tengo acceso a la fuente de la aplicación a o B y solo controla la base de datos y el disparador. ¿Hay algo que se pueda hacer con el desencadenador para que las actualizaciones hechas a num2 sean correctas en el caso de varias inserciones?

Solución:

A continuación se presenta la solución basada en el código de affan:

DECLARE @PROC_NEWNUM1 VARCHAR (10) 
DECLARE @NEWNUM2 numeric(20) 
DECLARE my_Cursor CURSOR FAST_FORWARD 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 

Marque aquí para un enfoque basado en el conjunto: SQL Server - Rewrite trigger to avoid cursor based approach

+2

A ** ** cursor dentro de un disparador ** ** ** es una mala idea terriblemente **; los cursores son notoriamente lentos y los cerdos de memoria, y cualquier cosa en un gatillo debe ser lo más delgado y rápido posible. Recomendaría ** fuertemente usar un enfoque diferente (de mi propia experiencia personal y agonizante con dichos constructos) –

+0

No podría 'si @ NEWNUM2 es una verificación nula' se reemplazará con' COALESCE (MAX (num2), 0) ' – JustAMartin

Respuesta

0

Simplemente tiene que abrir el cursor en INSERTED e iterarlo para @ PROC_NEWNUM1 y poner el resto del código en ese ciclo. por ejemplo

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

FETCH NEXT FROM @PROC_NEWNUM1; 


WHILE @@FETCH_STATUS = 0 
BEGIN FETCH NEXT FROM my_Cursor 
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 

END; 

CLOSE my_Cursor; DEALLOCATE my_Cursor; 
+0

Aunque lo anterior no es del todo correcto y tiene algunos errores, sí me ayudó al darme una base para trabajar. He agregado la versión actualizada de lo anterior en mi pregunta. – pug

+3

Lo siento, pero un cursor no es la mejor manera de hacerlo. – ErikE

+0

@ErikE entonces ... ¿qué es entonces? – Cocowalla

3

Tome un vistazo a inserted tabla de pseudo en su desencadenador, ya que contendrá múltiples filas durante estas operaciones. Siempre debe manejar múltiples filas en sus desencadenadores de todos modos.

Consulte aquí para obtener más información:

How to test for multiple row actions in a SQL Server trigger?

2

gatillo necesita ser rewriteen para manejar múltiples inserciones de fila. Nunca escriba un disparador como ese usando variables. Todos los desencadenadores deben considerar que algún día alguien va a hacer una inserción/actualización/eliminación de varias filas.

No deberías estar incrementando las columnas de esa manera en un desencadenador tampoco, si necesitas números de columna incrementados, ¿por qué no estás usando una columna de identidad?

+0

No me gustó la forma en que las columnas se estaban incrementando tampoco e incluso he cambiado el desencadenador en Oracle para usar una secuencia.En cuanto a la identidad, mi primer pensamiento fue utilizar alter table para cambiarlo a una identidad, pero desafortunadamente eso no es posible y otras formas de hacerlo requieren más cambios de los que soy lo suficientemente valiente como para arriesgarme. – pug

0
INSERT INTO dbo.media_queue (table_name, table_id, media_id, tunnel, sub_tunnel, event) 
     SELECT 
      'media_info' 
      ,i.id 
      ,i.session_id 
      ,i.tunnel 
      ,i.sub_tunnel 
      ,NULL 
     FROM INSERTED i; 
END 
Cuestiones relacionadas