2009-10-23 13 views
5

Per diverse tabelle con campi di identità, stiamo implementando uno schema di sicurezza a livello di riga utilizzando i trigger Viste e Anziché su tali viste. Ecco una struttura semplificata esempio:SQL Server - Ottieni valore di identità record inserito quando si utilizza una vista anziché trigger

-- 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 

Se voglio inserire un record e ottenere la sua identità, prima di attuare il RLS Invece di innesco, ho usato:

DECLARE @ItemId int; 

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

SELECT @ItemId = SCOPE_IDENTITY(); 

Con il grilletto, SCOPE_IDENTITY () non funziona più - restituisce NULL. Ho visto suggerimenti per l'utilizzo della clausola OUTPUT per recuperare l'identità, ma non riesco a farlo funzionare nel modo in cui ne ho bisogno. Se inserisco la clausola OUTPUT nella vista insert, non viene mai inserita nulla.

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

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

Se metto la clausola OUTPUT nel trigger sulla istruzione INSERT, il trigger restituisce il tavolo (posso vederlo da SQL Management Studio). Non riesco a catturarlo nel codice chiamante; o utilizzando una clausola OUTPUT su quella chiamata o utilizzando 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'); 

L'unica cosa che posso pensare è utilizzare la funzione IDENT_CURRENT(). Dal momento che ciò non funziona nell'ambito attuale, c'è un problema di utenti simultanei che inseriscono allo stesso tempo e lo incasinano. Se l'intera operazione è racchiusa in una transazione, ciò impedirebbe il problema di concorrenza?

BEGIN TRANSACTION 

DECLARE @ItemId int; 

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

SELECT @ItemId = IDENT_CURRENT('tblItem'); 

COMMIT TRANSACTION 

Qualcuno ha qualche suggerimento su come farlo meglio?

Conosco persone che leggeranno e dicono "I trigger sono MALE, non usarli!" Mentre apprezzo le tue convinzioni, per favore non offrire quel "suggerimento".

risposta

1

Si potrebbe provare SET CONTEXT_INFO dal trigger per essere letto da CONTEXT_INFO() nel client.

Lo usiamo nell'altro modo per passare le informazioni nel trigger ma funzioneremmo al contrario.

+1

Vedi il mio quesito, circa CONTEXT_INFO() utilizzare: http://stackoverflow.com/questions/1616229/contextinfo-and-convert –

+0

@ Rob: Ho aggiunto una risposta a questa – gbn

1

Hai provato in questo caso @@identity? Hai menzionato sia scope_Identity() che identity_current() ma non @@ identity.

+0

Buona idea. In questo caso, il solito "problema" dell'ambito potrebbe essere d'aiuto. – gbn

+0

Come sarebbe @@ IDENTITY essere migliore di IDENT_CURRENT()? Da quanto ho capito, mentre nessuno dei due si limita allo scopo del codice chiamante, @@ IDENTITY è l'ultimo valore di identità inserito, non importa dove. Quindi, se ho un trigger di controllo sulla tabella che inserisce un record in una tabella di controllo, @@ IDENTITY potrebbe restituire l'identità di quella riga (se ho capito bene). Ecco perché ho pensato che IDENT_CURRENT() fosse migliore perché almeno limitava lo "scope" alla tabella specifica. – CuppM

+0

@CuppM: @@ IDENTITY è per sessione, non per ambito. IDENT_CURRENT() non è né e potrebbe per qualsiasi sessione/ambito – gbn