2009-10-23 9 views
5

Pour plusieurs tables ayant des champs d'identité, nous implémentons un schéma de sécurité au niveau de la ligne utilisant les déclenchements Views et Instead de ces vues. Voici une structure exemple simplifié:SQL Server - Obtenir une valeur d'identité insérée lors de l'utilisation d'une vue à la place du déclencheur

-- Table 
CREATE TABLE tblItem (
    ItemId int identity(1,1) primary key, 
    Name varchar(20) 
) 
go 

-- View 
CREATE VIEW vwItem 
AS 
    SELECT * 
    FROM tblItem 
    -- RLS Filtering Condition 
go 

-- Instead Of Insert Trigger 
CREATE TRIGGER IO_vwItem_Insert ON vwItem 
INSTEAD OF INSERT 
AS BEGIN 
    -- RLS Security Checks on inserted Table 

    -- Insert Records Into Table 
    INSERT INTO tblItem (Name) 
    SELECT Name 
    FROM inserted; 
END 
go 

Si je veux insérer un enregistrement et obtenir son identité, avant la mise en œuvre du SJSR au lieu de détente, j'ai utilisé:

DECLARE @ItemId int; 

INSERT INTO tblItem (Name) 
VALUES ('MyName'); 

SELECT @ItemId = SCOPE_IDENTITY(); 

Avec la gâchette, SCOPE_IDENTITY () ne fonctionne plus - il retourne NULL. J'ai vu des suggestions pour utiliser la clause OUTPUT pour récupérer l'identité, mais je n'arrive pas à la faire fonctionner comme j'en ai besoin. Si je mets la clause OUTPUT sur la vue insert, rien n'y est jamais entré.

-- Nothing is added to @ItemIds 
DECLARE @ItemIds TABLE (ItemId int); 

INSERT INTO vwItem (Name) 
OUTPUT INSERTED.ItemId INTO @ItemIds 
VALUES ('MyName'); 

Si je mets la clause OUTPUT dans le déclencheur sur l'instruction INSERT, le déclencheur retourne la table (je peux voir de SQL Management Studio). Je n'arrive pas à le capturer dans le code appelant; soit en utilisant une clause OUTPUT sur cet appel ou en utilisant un SELECT * FROM().

-- Modified Instead Of Insert Trigger w/ Output 
CREATE TRIGGER IO_vwItem_Insert ON vwItem 
INSTEAD OF INSERT 
AS BEGIN 
    -- RLS Security Checks on inserted Table 

    -- Insert Records Into Table 
    INSERT INTO tblItem (Name) 
    OUTPUT INSERTED.ItemId 
    SELECT Name 
    FROM inserted; 
END 
go 

-- Calling Code 
INSERT INTO vwItem (Name) 
VALUES ('MyName'); 

La seule chose que je peux penser est d'utiliser la fonction IDENT_CURRENT(). Comme cela ne fonctionne pas dans la portée actuelle, il y a un problème d'insertion simultanée d'utilisateurs et de confusion. Si l'opération entière est enveloppée dans une transaction, cela empêcherait-il le problème de simultanéité?

BEGIN TRANSACTION 

DECLARE @ItemId int; 

INSERT INTO tblItem (Name) 
VALUES ('MyName'); 

SELECT @ItemId = IDENT_CURRENT('tblItem'); 

COMMIT TRANSACTION 

Quelqu'un at-il des suggestions sur la façon de faire mieux?

Je connais des gens qui liront ceci et diront "Les déclencheurs sont MAUVAIS, ne les utilisez pas!" Bien que j'apprécie vos convictions, n'offrez pas cette "suggestion".

Répondre

1

Vous pouvez essayer SET CONTEXT_INFO à partir du déclencheur à lire par CONTEXT_INFO() dans le client.

Nous l'utilisons de l'autre façon pour transmettre des informations dans le déclencheur, mais fonctionnerait en sens inverse.

+1

Voir ma question connexe sur CONTEXT_INFO() utiliser: http://stackoverflow.com/questions/1616229/contextinfo-and-convert –

+0

@Rob: J'ai ajouté une réponse – gbn

1

Avez-vous dans ce cas essayé @@ identité? Vous avez mentionné à la fois scope_Identity() et identity_current() mais pas @@ identity.

+0

Bonne réflexion. Le "problème" de portée habituel pourrait être une aide dans ce cas. – gbn

+0

Comment @ @ IDENTITY serait-il meilleur que IDENT_CURRENT()? D'après ce que je comprends, alors que ni l'un ni l'autre ne sont limités à la portée du code appelant, @@ IDENTITY est la dernière valeur d'identité insérée, peu importe où. Donc, si j'ai un déclencheur d'audit sur la table qui insère un enregistrement dans une table d'audit, @@ IDENTITY pourrait renvoyer l'identité de cette ligne (si je comprends bien). C'est pourquoi j'ai pensé que IDENT_CURRENT() était meilleur car il limitait au moins la "portée" à la table spécifique. – CuppM

+0

@CuppM: @@ IDENTITY est par session, pas par portée. IDENT_CURRENT() n'est ni et pourrait par aucune session/portée – gbn