Dalam menyiapkan jejak audit saya tidak memiliki masalah melacak siapa yang memperbarui atau menyisipkan catatan dalam sebuah tabel, namun, pelacakan yang menghapus catatan tampaknya lebih bermasalah.
Saya dapat melacak Sisipan / Pembaruan dengan memasukkan dalam bidang Sisipkan / Perbarui "Diperbarui oleh". Ini memungkinkan pemicu INSERT / UPDATE untuk memiliki akses ke bidang "UpdatedBy" via inserted.UpdatedBy
. Namun, dengan pemicu Hapus tidak ada data yang dimasukkan / diperbarui. Apakah ada cara untuk meneruskan informasi ke pemicu Hapus sehingga dapat mengetahui siapa yang menghapus catatan?
Berikut ini adalah pemicu Sisipkan / Perbarui
ALTER TRIGGER [dbo].[trg_MyTable_InsertUpdate]
ON [dbo].[MyTable]
FOR INSERT, UPDATE
AS
INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges)
VALUES (inserted.ID, inserted.LastUpdatedBy)
FROM inserted
Menggunakan SQL Server 2012
sql-server
sql-server-2012
trigger
audit
cacing web
sumber
sumber
SUSER_SNAME()
adalah kunci untuk mendapatkan siapa yang menghapus data.SUSER_SNAME()
akan berfungsi dalam situasi seperti aplikasi web di mana satu pengguna dapat digunakan untuk komunikasi database untuk seluruh aplikasi.Jawaban:
Ya: dengan menggunakan fitur yang sangat keren (dan kurang dimanfaatkan) disebut
CONTEXT_INFO
. Ini pada dasarnya adalah memori sesi yang ada di semua ruang lingkup dan tidak terikat oleh transaksi. Ini dapat digunakan untuk meneruskan info (info apa pun - yah, apa pun yang sesuai dengan ruang terbatas) untuk memicu serta bolak-balik antara panggilan sub-proc / EXEC. Dan saya telah menggunakannya sebelumnya untuk situasi yang sama persis ini.Info konteks adalah VARBINARY (128)
Setel melalui: SET CONTEXT_INFO
Dapatkan melalui: CONTEXT_INFO ()
Uji dengan yang berikut ini untuk melihat cara kerjanya. Perhatikan bahwa saya masuk
CHAR(128)
sebelumCONVERT(VARBINARY(128), ..
. Ini untuk memaksa pad-kosong agar lebih mudah untuk mengkonversi kembaliVARCHAR
ketika keluarCONTEXT_INFO()
karenaVARBINARY(128)
padded dengan0x00
s.Hasil:
MENEMPATKAN SEMUA BERSAMA:
Aplikasi harus memanggil prosedur tersimpan "Hapus" yang dilewati di UserName (atau apa pun) yang menghapus catatan. Saya berasumsi ini sudah model yang digunakan karena sepertinya Anda sudah melacak operasi Sisipkan dan Perbarui.
Prosedur "Hapus" yang disimpan tidak:
Pemicu audit tidak:
Harap perhatikan bahwa, seperti yang @SeanGallardy tunjukkan dalam komentar, karena prosedur lain dan / atau kueri ad hoc menghapus catatan dari tabel ini, ada kemungkinan bahwa:
CONTEXT_INFO
belum diatur dan masihNULL
:Untuk alasan ini saya telah memperbarui di atas
INSERT INTO AuditTable
untuk menggunakan nilaiCOALESCE
default. Atau, jika Anda tidak ingin default dan memerlukan nama, maka Anda dapat melakukan sesuatu yang mirip dengan:CONTEXT_INFO
telah disetel ke nilai yang bukan UserName yang valid, dan karenanya mungkin melebihi ukuranAuditTable.[UserWhoMadeChanges]
bidang:Untuk alasan ini saya menambahkan
LEFT
fungsi untuk memastikan bahwa apa pun yang diambilCONTEXT_INFO
tidak akan merusakINSERT
. Seperti disebutkan dalam kode, Anda hanya perlu mengatur50
ukuranUserWhoMadeChanges
bidang yang sebenarnya.PEMBARUAN UNTUK SQL SERVER 2016 DAN BARU
SQL Server 2016 menambahkan versi yang disempurnakan dari memori per-sesi ini: Session Context. Konteks Sesi baru pada dasarnya adalah tabel hash pasangan Key-Value dengan tipe "Key"
sysname
(yaituNVARCHAR(128)
) dan "Value"SQL_VARIANT
. Berarti:CONTEXT_INFO()
(untuk detail, silakan lihat posting saya: Mengapa Tidak CONTEXT_INFO () Mengembalikan Nilai Tepat yang Ditetapkan oleh SET CONTEXT_INFO? )CONTEXT_INFO
)Untuk detailnya, silakan lihat halaman dokumentasi berikut:
sumber
@@SPID
. Ini adalah memori PER-Sesi / Koneksi. Satu sesi tidak dapat menimpa informasi konteks sesi lain. Dan ketika sesi log off nilai hilang. Tidak ada yang namanya "item yang ditetapkan sebelumnya".Anda tidak bisa seperti itu, kecuali jika Anda ingin merekam ID pengguna server SQL daripada tingkat aplikasi.
Anda dapat melakukan soft delete dengan memiliki kolom bernama DeletedBy dan pengaturan yang diperlukan, maka pemicu pembaruan Anda dapat melakukan penghapusan yang sebenarnya (atau mengarsipkan catatan, saya biasanya menghindari penghapusan yang sulit jika mungkin dan legal) serta memperbarui jejak audit Anda . Untuk memaksa penghapusan dilakukan dengan cara itu, tentukan
on delete
pemicu yang memunculkan kesalahan. Jika Anda tidak ingin menambahkan kolom ke tabel fisik Anda, Anda bisa menentukan tampilan yang menambahkan kolom dan menentukaninstead of
pemicu untuk menangani memperbarui tabel dasar, tetapi itu mungkin berlebihan.sumber
SPARSE
kolom SQL Server ?Ya, ternyata ada dua cara ;-). Jika ada keraguan tentang penggunaan
CONTEXT_INFO
seperti yang saya sarankan dalam jawaban saya yang lain di sini , saya hanya memikirkan cara lain yang memiliki pemisahan fungsional yang lebih bersih dari kode / proses lain: gunakan tabel sementara lokal.Nama tabel temp harus menyertakan nama tabel yang dihapus karena itu akan membuatnya tetap terpisah dari kode lain yang mungkin dijalankan di sesi yang sama. Sesuatu di sepanjang garis:
#<TableName>DeleteAudit
Salah satu manfaat dari temp tabel lokal
CONTEXT_INFO
adalah bahwa jika seseorang di proc lain - yang entah bagaimana memanggil dari proc "Delete" ini - kebetulan salah menggunakan nama tabel temp yang sama, subproses akan a) membuat lokal baru tabel temp dari nama yang diminta yang akan terpisah dari tabel temp awal ini (meskipun memiliki nama yang sama), dan b) setiap pernyataan DML terhadap tabel temp lokal baru dalam sub-proses tidak akan mempengaruhi data apa pun di tabel temp lokal dibuat di sini dalam proses induk, karenanya tidak ada penimpaan data. Tentu saja, jika subproses mengeluarkan pernyataan DML terhadap nama tabel temp ini tanpa terlebih dahulu menerbitkan CREATE TABLE dengan nama yang sama, maka pernyataan DML tersebut akan memengaruhi data dalam tabel ini. NAMUN, pada titik ini kita benar - benar mendapatkanedge-casey di sini, bahkan lebih daripada dengan kemungkinan tumpang tindih penggunaanCONTEXT_INFO
(ya, saya tahu itu telah terjadi, itulah sebabnya saya mengatakan "edge-case" daripada "itu tidak akan pernah terjadi").Aplikasi harus memanggil prosedur tersimpan "Hapus" yang dilewati di UserName (atau apa pun) yang menghapus catatan. Saya berasumsi ini sudah model yang digunakan karena sepertinya Anda sudah melacak operasi Sisipkan dan Perbarui.
Prosedur "Hapus" yang disimpan tidak:
Pemicu audit tidak:
Saya telah menguji kode ini di pemicu dan berfungsi seperti yang diharapkan.
sumber