Comment obtenir la dernière ligne d'identité insérée lors de l'utilisation au lieu du déclencheur

9

Lorsque j'insère dans des tables en utilisant au lieu de déclencheurs @@Identity, IDENT_CURRENT('Table')et SCOPE_IDENTITY()retourne null. Comment puis-je obtenir la dernière identité de la ligne insérée?

mehdi lotfi
la source
Il n'y a insertedpas de ligne insérée lorsqu'un INSTEAD OFdéclencheur se déclenche.
ypercubeᵀᴹ
Vérifiez cette question SO: cela peut aider. stackoverflow.com/q/908257/27535
gbn
Vous devez choisir Select Id, .. dans Inserted, here Scope_Identity, @@ Identity will not work

Réponses:

8

Avec un déclencheur INSTEAD_OF, cela signifie qu'aucune insertion n'a encore eu lieu. Vous ne pouvez pas connaître l'identité car elle n'a pas encore été générée. Il est possible d'extraire la valeur des métadonnées ( DBCC CHECKIDENT), mais s'appuyer dessus ne fonctionnera pas correctement dans le cadre de la concurrence et, en outre, cela nécessite des privilèges élevés.

Les déclencheurs INSTEAD_OF sont extrêmement rarement requis et une odeur de code sérieuse. Êtes-vous sûr d'en avoir besoin? Vous ne pouvez pas faire le travail avec un déclencheur AFTER régulier?

Remus Rusanu
la source
Je souhaite contrôler l'intégrité des données de la ligne insérée. avant de les enregistrer. Si les données ne sont pas bonnes, je déclenche un message proportionnel d'erreur.
mehdi lotfi
2
Vous décrivez une clé étrangère. Il devrait être de la responsabilité de l'application d'insérer dans la table enfant, pas un déclencheur. Le faire à partir d'un déclencheur est une mauvaise conception, et de toute façon cela peut être fait à partir d'un déclencheur AFTER normal. Un déclencheur après peut générer des erreurs et provoquer une restauration, ce qui est la meilleure option qu'un déclencheur au lieu de.
Remus Rusanu
1
Quelle notion absurde - "au lieu des déclencheurs, une odeur de code sérieuse"? Ils sont très utiles par rapport à un déclencheur après - où, si vos règles métier sont violées, vous avez effectué le travail deux fois - vous avez inséré les lignes, puis vous les avez annulées. Un déclencheur au lieu de peut empêcher l'exécution du travail si vos règles métier ne peuvent pas être appliquées avec un DRI normal ou d'autres contraintes.
Aaron Bertrand
1
Tandis que "au lieu des déclencheurs sont une odeur sérieuse de code", n'est pas une règle stricte, elle est basée sur des problèmes réels possibles qu'elle peut causer dans un système complet. En général, les briseurs de règles métier ne devraient jamais se produire dans le système, sans parler d'atteindre le niveau de la base de données. Dans ce cas, une validation de règle dans le déclencheur n'est appropriée que comme dernier mécanisme de défense en cas de trou dans le système.
Alireza
4
L'intégrité déclarative est toujours meilleure qu'un déclencheur. Un déclencheur après est toujours meilleur qu'un déclencheur au lieu d'un. Au lieu de cela, les déclencheurs ont un comportement `` génial '' dans de nombreuses situations, ils sont opaques pour accéder aux optimisations de chemin dans DML, ils font que les niveaux d'isolement se comportent de manière irrégulière. Au lieu de déclencheurs crier «J'aurais dû être une procédure stockée d'accès à la place». Et je n'achète pas du tout l'argument «faire le travail deux fois», l'optimisation du chemin d' exception ne devrait pas influencer la conception, surtout au prix du ralentissement du chemin fréquent .
Remus Rusanu
12

Dans votre lieu de déclencheur, vous pouvez certainement obtenir la valeur insérée ... mais pas avant d'avoir effectué l'insertion.

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

Résultats:

id
----
1

Maintenant nettoyez:

DROP TABLE dbo.SmellThis;

En passant, vous ne devriez jamais, jamais, jamais utiliser @@IDENTITYou de IDENT_CURRENT()toute façon. Et SCOPE_IDENTITYdevrait être réservé aux situations où vous savez qu'une seule ligne peut être insérée. Une idée fausse commune avec les déclencheurs est qu'ils se déclenchent par ligne, comme dans d'autres plates-formes, mais dans SQL Server, ils se déclenchent par opération - donc une insertion à plusieurs lignes en utilisant VALUES(),(),()ou INSERT...SELECT- que SCOPE_IDENTITYdéfiniriez-vous pour votre variable?

Aaron Bertrand
la source
Comment j'enregistre le résultat de l'enregistrement inséré dans la table variable pour une utilisation ultérieure.
mehdi lotfi
@mehdi pouvez-vous définir "plus tard"? Et ne pouvez-vous pas ajouter des colonnes à la variable de table que j'ai déjà déclarée ci-dessus?
Aaron Bertrand
3
J'adore vos noms de table.
Dan Esparza
-1

Problème principal: Trigger et Entity Framework fonctionnent tous les deux dans une portée différente. Le problème est que si vous générez une nouvelle valeur PK dans le déclencheur, la portée est différente. Ainsi, cette commande ne renvoie aucune ligne et EF lève une exception.

La solution consiste à ajouter l'instruction SELECT suivante à la fin de votre déclencheur:

SELECT * FROM deleted UNION ALL
SELECT * FROM inserted;

au lieu de *, vous pouvez mentionner tout le nom de la colonne, y compris

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