SQL Server - получить значение идентификатора вставленной записи при использовании представления вместо триггера - PullRequest
5 голосов
/ 23 октября 2009

Для нескольких таблиц, имеющих поля идентичности, мы реализуем схему защиты на уровне строк, используя представления и триггеры вместо этих представлений. Вот упрощенная примерная структура:

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

Если я хочу вставить запись и получить ее идентификатор, перед реализацией триггера RLS вместо я использовал:

DECLARE @ItemId int;

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

SELECT @ItemId = SCOPE_IDENTITY();

С триггером SCOPE_IDENTITY () больше не работает - он возвращает NULL. Я видел предложения по использованию предложения OUTPUT для восстановления идентичности, но я не могу заставить его работать так, как мне нужно. Если я добавлю предложение OUTPUT к представлению вставки, в него ничего не будет введено.

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

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

Если я добавлю предложение OUTPUT в триггер оператора INSERT, триггер вернет таблицу (я могу просмотреть ее в SQL Management Studio). Я не могу захватить это в вызывающем коде; либо с помощью предложения OUTPUT для этого вызова, либо с помощью 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');

Единственное, о чем я могу думать, это использовать функцию IDENT_CURRENT (). Так как это не работает в текущей области, существует проблема одновременного вставления пользователей и их путаницы. Если вся операция заключена в транзакцию, это предотвратит проблему параллелизма?

BEGIN TRANSACTION

DECLARE @ItemId int;

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

SELECT @ItemId = IDENT_CURRENT('tblItem');

COMMIT TRANSACTION

У кого-нибудь есть предложения, как сделать это лучше?

Я знаю людей, которые прочтут это и скажут: «Триггеры - ЗЛО, не используйте их!» Хотя я ценю ваши убеждения, пожалуйста, не предлагайте это «предложение».

Ответы [ 2 ]

1 голос
/ 23 октября 2009

Пробовали ли вы в этом случае @@ тождество? Вы упомянули и scope_Identity (), и identity_current (), но не @@ identity.

1 голос
/ 23 октября 2009

Вы можете попробовать SET CONTEXT_INFO из триггера для чтения CONTEXT_INFO() в клиенте.

Мы используем его другим способом для передачи информации в триггер, но будем работать в обратном порядке.

...