Cómo insertar la última fila de identidad cuando se usa en lugar del desencadenador

9

Cuando inserto en tablas usando en lugar de disparadores @@Identity, IDENT_CURRENT('Table')y SCOPE_IDENTITY()devuelvo nulo. ¿Cómo puedo obtener la última identidad de la fila insertada?

mehdi lotfi
fuente
No hay insertedninguna fila insertada cuando se INSTEAD OFactiva un disparador.
ypercubeᵀᴹ
Verifique esta pregunta SO: puede ayudar. stackoverflow.com/q/908257/27535
gbn
Debe elegir Seleccionar ID, ... de Insertado, aquí Scope_Identity, @@ Identity no funcionará

Respuestas:

8

Con un desencadenador INSTEAD_OF significa que aún no se ha insertado. No puede conocer la identidad ya que aún no se generó. Es posible escabullir el valor de los metadatos ( DBCC CHECKIDENT), pero confiar en él no funcionará correctamente bajo concurrencia y además requiere privilegios elevados.

Los desencadenadores INSTEAD_OF rara vez se requieren y un olor a código serio. ¿Estás seguro de que lo necesitas? ¿No puedes hacer el trabajo con un disparador DESPUÉS regular?

Remus Rusanu
fuente
Quiero controlar la integridad de los datos de la fila insertada. antes de salvarlos. Si los datos no son buenos, subo un mensaje proporcional de error.
mehdi lotfi
2
Estás describiendo una clave foránea. Debería ser responsabilidad de la aplicación insertar en la tabla secundaria, no un activador. Hacerlo desde un disparador es un mal diseño, y de todos modos se puede hacer desde un disparador normal DESPUÉS. Un desencadenante posterior puede generar errores y provocar una reversión, que es la mejor opción que un desencadenante en lugar de.
Remus Rusanu
1
¿Qué idea más absurda: "en lugar de desencadenantes, hay un olor a código serio"? Son muy útiles en comparación con un disparador posterior, donde, si se violan las reglas de su negocio, ha realizado el trabajo dos veces, ha insertado las filas y luego las ha revertido. Un desencadenante en lugar de un disparador puede evitar que suceda el trabajo si las reglas de su negocio no se pueden aplicar con DRI normal u otras restricciones.
Aaron Bertrand
1
Si bien "en lugar de los desencadenantes es un olor a código serio", no es una regla estricta, se basa en los posibles problemas reales que puede causar en un sistema completo. En general, los que rompen las reglas de negocio nunca deberían ocurrir en el sistema, y ​​mucho menos llegar al nivel de la base de datos. En ese caso, una validación de la regla en el disparador solo es apropiada como último mecanismo de defensa en caso de que haya un agujero en el sistema.
Alireza
44
La integridad declarativa siempre es mejor que un disparador. Un disparador posterior siempre es mejor que un disparador en lugar de disparador. En lugar de que los desencadenantes tengan un comportamiento "funky" en muchas situaciones, son opacos para acceder a las optimizaciones de ruta en DML, hacen que los niveles de aislamiento se comporten de manera errática. En lugar de disparadores, grita "Debería haber sido un procedimiento almacenado de acceso en su lugar". Y no compro el argumento 'hacer el trabajo dos veces' en absoluto, la optimización de la ruta de excepción no debería influir en el diseño, especialmente a costa de ralentizar la ruta frecuente .
Remus Rusanu
12

En su activador en lugar de en el disparador, definitivamente puede obtener el valor insertado ... pero no hasta después de haber realizado la inserción.

USE tempdb;
GO

CREATE TABLE dbo.SmellThis
(
  id INT IDENTITY(1,1),
  name VARCHAR(32)
);
GO

CREATE TRIGGER dbo.SmellThis_First
ON dbo.SmellThis
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @ids TABLE(id INT);

    IF NOT EXISTS 
    (
      SELECT 1 FROM sys.objects AS o
        INNER JOIN inserted AS i
        ON o.name = i.name
    )
    INSERT dbo.SmellThis(name)  
      OUTPUT inserted.id INTO @ids
      SELECT name 
      FROM inserted;

    SELECT id FROM @ids;
END
GO

INSERT dbo.SmellThis(name) SELECT 'Remus';
GO

Resultados:

id
----
1

Ahora limpia:

DROP TABLE dbo.SmellThis;

Por otro lado, nunca, nunca, nunca deberías estar usando @@IDENTITYo de IDENT_CURRENT()todos modos. Y SCOPE_IDENTITYdebe reservarse para situaciones en las que sabe que solo se puede insertar una fila. Una idea errónea común con los desencadenantes es que se disparan por fila, como en otras plataformas, pero en SQL Server se disparan por operación, por lo que una inserción de varias filas usando VALUES(),(),()o INSERT...SELECT, SCOPE_IDENTITY¿qué establecería en su variable?

Aaron Bertrand
fuente
Cómo guardo el resultado del registro insertado en la tabla de variables para su uso posterior.
mehdi lotfi
@mehdi ¿puedes definir "más tarde"? ¿Y no puede agregar columnas a la variable de tabla que ya he declarado anteriormente?
Aaron Bertrand
3
Me encantan tus nombres de mesa.
Dan Esparza
-1

Problema principal: el marco de activación y entidad funciona en un alcance diferente. El problema es que si genera un nuevo valor de PK en el disparador, el alcance es diferente. Por lo tanto, este comando devuelve cero filas y EF arrojará una excepción.

La solución es agregar la siguiente instrucción SELECT al final de su Trigger:

SELECT * FROM deleted UNION ALL
SELECT * FROM inserted;

en lugar de * puede mencionar todo el nombre de la columna, incluido

SELECT IDENT_CURRENT(‘tablename’) AS <IdentityColumnname>
Ashish Mishra
fuente