2009-10-23 4 views
5

Für mehrere Tabellen mit Identitätsfeldern implementieren wir ein Sicherheitsschema auf Zeilenebene mit Views und anstelle von Triggern für diese Views. Hier ist ein vereinfachtes Beispiel Struktur:SQL Server - Datensatzidentitätswert bei Verwendung eines Views statt eines Triggers erhalten

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

Wenn ich einen Datensatz eingefügt werden soll und seine Identität bekommen, bevor die RLS Instead Of-Trigger Implementierung, habe ich:

DECLARE @ItemId int; 

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

SELECT @ItemId = SCOPE_IDENTITY(); 

Mit dem Trigger SCOPE_IDENTITY () funktioniert nicht mehr - es gibt NULL zurück. Ich habe Vorschläge für die Verwendung der OUTPUT-Klausel gesehen, um die Identität zurück zu bekommen, aber ich kann nicht scheinen, dass es so funktioniert, wie ich es brauche. Wenn ich die OUTPUT-Klausel auf den View-Insert setze, wird nichts mehr eingegeben.

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

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

Wenn ich die OUTPUT-Klausel in dem Trigger auf der INSERT-Anweisung setzte, gibt den Auslöser die Tabelle (ich es von SQL Management Studio anzeigen kann). Ich kann es anscheinend nicht im Anrufcode erfassen; entweder mit einer OUTPUT-Klausel für diesen Aufruf oder mit SELECT * FROM().

Das einzige, was ich mir vorstellen kann, ist die IDENT_CURRENT() -Funktion zu verwenden. Da dies im aktuellen Bereich nicht funktioniert, gibt es ein Problem, dass gleichzeitige Benutzer gleichzeitig einfügen und es vermasseln. Wenn die gesamte Operation in eine Transaktion eingeschlossen wird, würde dies das Problem der Parallelität verhindern?

BEGIN TRANSACTION 

DECLARE @ItemId int; 

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

SELECT @ItemId = IDENT_CURRENT('tblItem'); 

COMMIT TRANSACTION 

Hat jemand irgendwelche Vorschläge, wie man das besser macht?

Ich kenne Leute da draußen, die das lesen werden und sagen: "Trigger sind BÖSE, benutze sie nicht!" Während ich Ihre Überzeugungen schätze, bieten Sie diesen "Vorschlag" bitte nicht an.

Antwort

1

Sie könnten versuchen SET CONTEXT_INFO aus dem Trigger von CONTEXT_INFO() im Client gelesen werden.

Wir verwenden es den anderen Weg, um Informationen in den Trigger zu übergeben, aber würde umgekehrt arbeiten.

+1

Siehe meine ähnliche Frage zu CONTEXT_INFO() verwenden: http://stackoverflow.com/questions/1616229/contextinfo-and-convert –

+0

@ Rob: Ich habe eine Antwort auf diese – gbn

1

Haben Sie in diesem Fall @@ Identität versucht? Sie haben sowohl scope_Identity() als auch identity_current(), aber nicht @@ identity erwähnt.

+0

Gute Idee. Das übliche Scoping- "Problem" könnte in diesem Fall hilfreich sein. – gbn

+0

Wie wäre @@ IDENTITY besser als IDENT_CURRENT()? Soweit ich weiß, ist @ IDENTITY der letzte eingegebene Identitätswert, egal wo. Wenn ich also einen Audit-Trigger für die Tabelle habe, der einen Datensatz in eine Audit-Tabelle einfügt, könnte @@ IDENTITY die Identität dieser Zeile zurückgeben (wenn ich das richtig verstehe). Deshalb dachte ich IDENT_CURRENT() war besser, weil es den "Geltungsbereich" zumindest auf die spezifische Tabelle beschränkte. – CuppM

+0

@CuppM: @@ IDENTITY ist pro Sitzung, nicht pro Bereich. IDENT_CURRENT() ist weder und könnte durch irgendeine Sitzung/Bereich – gbn

Verwandte Themen