2009-10-23 10 views
5

W przypadku kilku tabel z polami tożsamości wdrażamy schemat zabezpieczeń poziomu wiersza z widokami i zamiast wyzwalaczy dla tych widoków. Oto uproszczony przykład struktura:Serwer SQL - Uzyskaj wstawioną wartość tożsamości rekordu podczas korzystania z widoku zamiast wyzwalacza

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

Jeśli chcę wstawić rekord i dostać swoją tożsamość, przed wdrożeniem RLS zamiast spustu, użyłem:

DECLARE @ItemId int; 

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

SELECT @ItemId = SCOPE_IDENTITY(); 

Przy spuście, SCOPE_IDENTITY () już nie działa - zwraca NULL. Widziałem sugestie użycia klauzuli OUTPUT, aby odzyskać tożsamość, ale nie mogę sprawić, by działała tak, jak tego potrzebuję. Jeśli wstawię klauzulę OUTPUT do wstawki widoku, nic nigdy nie zostanie do niej wprowadzone.

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

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

Jeśli mogę umieścić klauzulę wyjście w wyzwalacz na INSERT, spust zwraca tabelę (mogę go zobaczyć z SQL Management Studio). Nie mogę uchwycić tego w kodzie wywołującym; poprzez użycie klauzuli OUTPUT dla tego połączenia lub użycie 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'); 

Jedyne, co mogę myśleć, to użyć funkcji IDENT_CURRENT(). Ponieważ nie działa w bieżącym zakresie, pojawia się problem równoczesnego wstawiania użytkowników i zmyślania. Jeśli cała operacja jest zawijana w transakcji, czy to zapobiegnie problemowi współbieżności?

BEGIN TRANSACTION 

DECLARE @ItemId int; 

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

SELECT @ItemId = IDENT_CURRENT('tblItem'); 

COMMIT TRANSACTION 

Czy ktoś ma jakieś sugestie, jak to zrobić lepiej?

Znam ludzi, którzy to przeczytają i powiedzą: "Wyzwalacze to ZŁY, nie używajcie ich!" Doceniamy twoje przekonania, ale nie proponuj tej "sugestii".

Odpowiedz

1

Możesz spróbować SET CONTEXT_INFO z wyzwalacza, który zostanie odczytany przez klienta w postaci CONTEXT_INFO().

Używamy go w inny sposób, aby przekazać informacje do wyzwalacza, ale działałoby odwrotnie.

+1

Zobacz moją powiązane pytanie o CONTEXT_INFO() użyć: http://stackoverflow.com/questions/1616229/contextinfo-and-convert –

+0

@Rob: dodałem odpowiedź do tej – gbn

1

Czy w tym przypadku próbowałeś tożsamości @@? Wspominałeś o tożsamościach scope_Identity() i identity_current(), ale nie @@.

+0

Dobrego myślenia. Typowy "problem" dotyczący zakresu może być w tym przypadku pomocą. – gbn

+0

W jaki sposób @@ TOŻSAMOŚĆ byłaby lepsza niż IDENT_CURRENT()? Z tego, co rozumiem, nie ograniczając się do zakresu kodu wywołującego, @@ TOŻSAMOŚĆ jest ostatnią wstawioną wartością tożsamości bez względu na to, gdzie. Więc jeśli mam wyzwalacz kontroli w tabeli, który wstawia rekord do tabeli kontroli, @@ TOŻSAMOŚĆ może zwrócić tożsamość tego wiersza (jeśli dobrze rozumiem). Dlatego uznałem, że IDENT_CURRENT() jest lepszy, ponieważ przynajmniej ogranicza "zakres" do konkretnej tabeli. – CuppM

+0

@CuppM: @@ TOŻSAMOŚĆ jest na sesję, a nie na zakres. IDENT_CURRENT() nie jest i może być przez dowolną sesję/zakres – gbn

Powiązane problemy