Para varias tablas que tienen campos de identidad, estamos implementando un esquema de seguridad de nivel de fila utilizando los disparadores Vistas y En lugar de en esas vistas. Aquí es una estructura simplificada ejemplo:Servidor SQL: obtenga el valor de identidad de registro insertado cuando utilice una vista en lugar de desencadenar
-- 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 quiero insertar un registro y obtener su identidad, antes de la aplicación de la RLS En vez de gatillo, que utiliza:
DECLARE @ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT @ItemId = SCOPE_IDENTITY();
Con el gatillo, SCOPE_IDENTITY () ya no funciona - devuelve NULL. He visto sugerencias para usar la cláusula OUTPUT para recuperar la identidad, pero parece que no puedo hacer que funcione de la manera que lo necesito. Si pongo la cláusula OUTPUT en la inserción de vista, nunca se ingresa nada.
-- Nothing is added to @ItemIds
DECLARE @ItemIds TABLE (ItemId int);
INSERT INTO vwItem (Name)
OUTPUT INSERTED.ItemId INTO @ItemIds
VALUES ('MyName');
Si pongo la cláusula de salida en el gatillo de la instrucción INSERT, el gatillo devuelve la mesa (puedo verlo desde SQL Server Management Studio). Parece que no puedo capturarlo en el código de llamada; ya sea usando una cláusula OUTPUT en esa llamada o usando 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');
Lo único que se me ocurre es usar la función IDENT_CURRENT(). Como eso no funciona en el ámbito actual, hay un problema de usuarios concurrentes que se insertan al mismo tiempo y lo estropean. Si toda la operación está envuelta en una transacción, ¿eso evitaría el problema de concurrencia?
BEGIN TRANSACTION
DECLARE @ItemId int;
INSERT INTO tblItem (Name)
VALUES ('MyName');
SELECT @ItemId = IDENT_CURRENT('tblItem');
COMMIT TRANSACTION
¿Alguien tiene alguna sugerencia sobre cómo hacerlo mejor?
Conozco gente que leerá esto y dirá "Los desencadenantes son MALVADOS, ¡no los use!" Si bien aprecio sus convicciones, no ofrezca esa "sugerencia".
Consulte mi pregunta relacionada sobre CONTEXT_INFO() use: http://stackoverflow.com/questions/1616229/contextinfo-and-convert –
@Rob: He añadido una respuesta a esta – gbn