You are on page 1of 3

Triggers y claves duplicadas

[otro punto de vista]

Fecha: 13/Dic/2005 (12/12/2005)


Autor: Jorge Martinez Foronda - mafoj70@hotmail.com

Este pequeño articulo pretende dar una ligera visión sobre el uso de los
triggers para poder controlar errores de clave duplicadas. Muchos os preguntareis
porque vamos a controlar nosotros las claves duplicadas si tenemos Primary Keys e
índices únicos que ya se encargan de esto por nosotros.
Una de las razones que me llevo a realizar esta prueba, que ahora pongo en vuestro
conocimiento, fue la casualidad y la curiosidad (que orden lleva SQL Server cuando se
inserta un registro en una tabla).
Me puse manos a la obra, el mono de trabajo y música....
Los motivos principales para este estudio son 3'5: - saber cuales son las claves que se
están intentando insertar de forma duplicada en una tabla - evitar mensajes de error por
errores 2627 - controlar la inserción masiva de datos desde otras fuentes - y tb el "tapar"
pequeños despistes en nuestras aplicaciones
Adjunto los scripts necesarios para realizar la practica. Creo que en el script están bien
explicados todos los pasos.
La idea es básica, creo un Trigger del tipo INSTEAD OF INSERT que se ejecuta
siempre y en vez de cada INSERT que se lance sobre mi tabla. En este Trigger hago una
comprobación de los datos de la PK que se están intentando insertar en la tabla, si es así
guardo esta información en una tabla temporal POPO (para tener un histórico) en caso
contrario prosigo con la inserción. Una característica de este tipo de Triggers es que
solo se ejecutan una vez por INSERT de manera que no se producen ejecuciones
anidadas de Triggers del tipo INSERT.
Como podréis ver el ejemplo es bastante sencillo, era mi intención, y las aplicaciones
las tendréis que ir buscando vosotros (no lo voy a hacer yo todo :-) )

Los pasos a ejecutar el Script son:

-- Creamos la BBDD de trabajo


CREATE DATABASE Temporal
GO

-- Usamos la BBDD
USE Temporal
GO

-- Creamos la tabla de trabajo


CREATE TABLE [dbo].[tabla] (
[Numero] [int] NOT NULL ,
[Texto] [varchar] (10) COLLATE Modern_Spanish_CI_AI NULL
) ON [PRIMARY]
GO
-- Creamos una clave primaria
ALTER TABLE [dbo].[tabla] ADD
CONSTRAINT [PK_tabla] PRIMARY KEY CLUSTERED
(
[Numero]
) ON [PRIMARY]
GO

-- Creamos la tabla de trabajo auxiliar


CREATE TABLE [dbo].[Popo] (
[Numero] [int] NOT NULL ,
[Texto] [varchar] (10) COLLATE Modern_Spanish_CI_AI NULL ,
[Fecha] [Datetime]
) ON [PRIMARY]
GO

-- Creamos el Trigger INSTED OF INSERT sobre la tabla de trabajo


CREATE TRIGGER trgIns ON [dbo].[tabla]
INSTEAD OF INSERT

AS

-- Me guardo las que ya estan en la Tabla


INSERT INTO Popo SELECT Numero,Texto,GetDate() FROM Inserted I
WHERE EXISTS(SELECT Numero FROM Tabla WHERE Numero=I.Numero)

-- Me guardo los intentos de insrtar claves duplicadas mediante


conjuntos de resultados
INSERT INTO Popo SELECT Numero,Texto,GetDate() FROM Inserted I
WHERE EXISTS(SELECT numero FROM inserted WHERE numero=i.numero GROUP
BY numero HAVING COUNT(*)>1)

-- Inserto las que no estan en la tabla y en el caso que esten


intentando meter un conjunto de resultados (via UNION ALL) no meto
ninguno de
-- los que ya vinieran duplicados en este origen.
INSERT INTO Tabla SELECT Numero,Texto FROM Inserted I
WHERE NOT EXISTS(SELECT Numero FROM Tabla WHERE Numero=I.Numero)
AND EXISTS(SELECT numero FROM inserted WHERE numero=i.numero GROUP
BY numero HAVING COUNT(*)=1)

GO

-- Metemos algunos registros en la tabla que no van a producir valores


duplicados
INSERT INTO dbo.Tabla VALUES (1,'Uno')
INSERT INTO dbo.Tabla VALUES (2,'Dos')
INSERT INTO dbo.Tabla VALUES (3,'Tres')
INSERT INTO dbo.Tabla VALUES (4,'Cuatro')
INSERT INTO dbo.Tabla VALUES (5,'Cinco')

-- Ahora utilizamos una sentencia del tipo UNION ALL, para insertar un
conjunto de resultados
INSERT INTO dbo.tabla
SELECT 1,'uno'
UNION ALL
SELECT 2,'dos'
UNION ALL
SELECT 1,'uno'
UNION ALL
SELECT 8,'dos'

-- Ahora forzamos duplicados


INSERT INTO dbo.Tabla VALUES (1,'Uno')
INSERT INTO dbo.Tabla VALUES (2,'Dos')
INSERT INTO dbo.Tabla VALUES (3,'Tres')
INSERT INTO dbo.Tabla VALUES (4,'Cuatro')

-- Vemos como van las tablas


SELECT * FROM dbo.tabla
SELECT * FROM dbo.popo

-- Para vaciarlas
DELETE FROM dbo.tabla

-- Creamos un Procedimiento Almacenado


CREATE PROCEDURE dbo.Inserta
AS
-- Voy a meter un registro duplicado
INSERT INTO dbo.Tabla VALUES (4,'Cuatro')
-- Ahora uno que sea diferente y que produciria un error 2627
INSERT INTO dbo.Tabla VALUES (5,'Cinco')
GO

-- Ejecutamos el SP
EXEC dbo.Inserta
GO

-- Eliminamos la BBDD de trabajo


USE MASTER
GO

DROP DATABASE Temporal


GO

Por supuesto que este procedimiento en términos de rendimiento pues no es la mejor


solución, en un próximo estudio veremos las diferencias en cuanto a este punto se
producen. Agradezco cualquier tipo de comentario, constructivo y/o destructivo y
espero que os haya servido para algo.

You might also like