Czy istnieje sposób na przekazanie informacji do wyzwalacza usuwania, aby mógł wiedzieć, kto usunął rekord?
Tak: używając bardzo fajnej (i niewykorzystanej funkcji) o nazwie CONTEXT_INFO
. Zasadniczo jest to pamięć sesji, która istnieje we wszystkich zakresach i nie jest związana transakcjami. Może być używany do przekazywania informacji (dowolnych informacji - cóż, które mieszczą się w ograniczonej przestrzeni) do wyzwalaczy, a także w obie strony między wywołaniami sub-proc / EXEC. Użyłem go już wcześniej w tej samej sytuacji.
Przetestuj poniższe, aby zobaczyć, jak to działa. Zauważ, że przechodzę CHAR(128)
na CONVERT(VARBINARY(128), ..
. Ma to na celu wymuszenie wypełniania pustego pola, aby ułatwić powrót do niego VARCHAR
po wyjściu, CONTEXT_INFO()
ponieważ VARBINARY(128)
jest on odpowiednio wypełniony za pomocą 0x00
s.
SELECT CONTEXT_INFO();
-- Initially = NULL
DECLARE @EncodedUser VARBINARY(128);
SET @EncodedUser = CONVERT(VARBINARY(128),
CONVERT(CHAR(128), 'I deleted ALL your records! HA HA!')
);
SET CONTEXT_INFO @EncodedUser;
SELECT CONTEXT_INFO() AS [RawContextInfo],
RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())) AS [DecodedUser];
Wyniki:
0x492064656C6574656420414C4C20796F7572207265636F7264732120484120484121202020202020...
I deleted ALL your records! HA HA!
KŁADĄC WSZYSTKO RAZEM:
Aplikacja powinna wywołać procedurę przechowywaną „Usuń”, która przechodzi w nazwę użytkownika (lub cokolwiek innego), która usuwa rekord. Zakładam, że jest to już używany model, ponieważ wygląda na to, że śledzisz już operacje wstawiania i aktualizacji.
Procedura przechowywana „Usuń”:
DECLARE @EncodedUser VARBINARY(128);
SET @EncodedUser = CONVERT(VARBINARY(128),
CONVERT(CHAR(128), @UserName)
);
SET CONTEXT_INFO @EncodedUser;
-- DELETE STUFF HERE
Wyzwalacz kontroli:
-- Set the INT value in LEFT (currently 50) to the max size of [UserWhoMadeChanges]
INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges)
SELECT del.ID, COALESCE(
LEFT(RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())), 50),
'<unknown>')
FROM DELETED del;
Należy pamiętać, że jak zauważył @SeanGallardy w komentarzu, z powodu innych procedur i / lub zapytań ad hoc usuwających rekordy z tej tabeli, możliwe jest, że:
CONTEXT_INFO
nie został ustawiony i nadal jest NULL
:
Z tego powodu zaktualizowałem powyższe, INSERT INTO AuditTable
aby użyć COALESCE
domyślnej wartości. Lub, jeśli nie chcesz wartości domyślnej i potrzebujesz nazwy, możesz zrobić coś podobnego do:
DECLARE @UserName VARCHAR(50); -- set to the size of AuditTable.[UserWhoMadeChanges]
SET @UserName = LEFT(RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())), 50);
IF (@UserName IS NULL)
BEGIN
ROLLBACK TRAN; -- cancel the DELETE operation
RAISERROR('Please set UserName via "SET CONTEXT_INFO.." and try again.', 16 ,1);
END;
-- use @UserName in the INSERT...SELECT
CONTEXT_INFO
została ustawiona na wartość, która nie jest prawidłową nazwą użytkownika, a zatem może przekraczać rozmiar AuditTable.[UserWhoMadeChanges]
pola:
Z tego powodu dodałem LEFT
funkcję zapewniającą, że cokolwiek zostanie złapane, CONTEXT_INFO
nie złamie INSERT
. Jak zaznaczono w kodzie, wystarczy ustawić 50
rzeczywisty rozmiar UserWhoMadeChanges
pola.
AKTUALIZACJA DLA SQL Server 2016 i nowszych
SQL Server 2016 dodał ulepszoną wersję tej pamięci na sesję: kontekst sesji. Nowy kontekst sesji jest w zasadzie tabelą skrótów par klucz-wartość z „kluczem” będącym typem sysname
(tj. NVARCHAR(128)
) I „wartością” SQL_VARIANT
. Znaczenie:
- Istnieje teraz rozdział wartości, więc mniejsze prawdopodobieństwo konfliktu z innymi zastosowaniami
- Możesz przechowywać różne typy, nie martwiąc się już o dziwne zachowanie podczas odzyskiwania wartości za pośrednictwem
CONTEXT_INFO()
(szczegółowe informacje znajdują się w moim poście: Dlaczego CONTEXT_INFO () Zwraca dokładną wartość ustawioną przez SET CONTEXT_INFO? )
- Dostajesz o wiele więcej miejsca: maks. 8000 bajtów na „wartość”, łącznie do 256 kb na wszystkie klucze (w porównaniu do maks. 128 bajtów
CONTEXT_INFO
)
Aby uzyskać szczegółowe informacje, zobacz następujące strony dokumentacji:
SUSER_SNAME()
jest kluczem do ustalenia, kto usunął rekord.