SQL Server: Bagaimana menonaktifkan pemicu hanya untuk pembaruan untuk sesi Anda saat ini?

15

Saya bekerja pada SQL Server 2008 R2.

Saya memiliki manfaat tabel yang memiliki SETELAH INSERT, pemicu UPDATE bernama tiu_benefit .

Saya ingin menulis pernyataan UPDATE untuk tabel ini untuk memperbarui 1 baris tetapi saya tidak ingin pemicu untuk menyala. Saya tahu saya dapat menonaktifkan pemicu sebelum UPDATE dan kemudian mengaktifkan pelatuk setelah UPDATE:

DISABLE TRIGGER tiu_benefit ON benefit;  
GO  
UPDATE benefit SET editor = 'srh' where benefit_id = 9876
GO
ENABLE TRIGGER tiu_benefit ON benefit;  
GO  

Tapi ini menonaktifkan dan mengaktifkan memicu akan mempengaruhi semua pengguna yang masuk saat ini. Jadi ada kemungkinan pengguna lain menjalankan UPDATE / INSERT ketika pemicu dinonaktifkan oleh skrip saya yang tidak bagus. Karena itulah saya hanya ingin menonaktifkan dan mengaktifkan pemicu untuk sesi saya saat ini. Apa itu mungkin? Jika ya tolong beri tahu caranya.

Terima kasih

srh
sumber
1
Jika Anda tidak dapat mengubah pemicu Anda, maka jawabannya adalah tidak.
jyao

Jawaban:

6

Saya melakukan beberapa pengujian pada ini dan saya pikir Anda akan baik-baik saja jika Anda menjalankan proses Anda dalam satu transaksi.

BEGIN TRANSACTION
GO

DISABLE TRIGGER tiu_benefit ON benefit;
GO

UPDATE benefit
SET editor = 'srh'
WHERE benefit_id = 9876
GO

ENABLE TRIGGER tiu_benefit ON benefit;
GO

--Decide to commit or rollback

--commit
--rollback 

Dalam pengujian saya, saya hanya menyoroti dan mengeksekusi BEGIN TRANSACTIONdan yang DISABLE TRIGGERpertama. Saya kemudian membuka baru (kedua) jendela query dan mencoba untuk menjalankan berbagai pernyataan DML ( SELECT, INSERT, UPDATE DELETE) terhadap tabel basis. Semua upaya untuk mengakses tabel dasar di jendela kueri kedua menunggu kunci yang dipegang oleh jendela dengan transaksi eksplisit. Setelah saya melakukan (atau memutar kembali) transaksi eksplisit saya, jendela kedua dapat mengakses tabel.

Scott Hodgin
sumber
Ini akan berhasil, tetapi kunci dapat menyebabkan masalah yang tidak diinginkan di hilir, tergantung pada berapa lama Anda menjaga transaksi terbuka.
CaM
@CaM - Saya akan berasumsi bahwa pembaruan satu baris tidak akan terlalu lama dengan asumsi OP melakukan atau memutar kembali transaksi dengan cepat. Mudah-mudahan, ada indeks pada benefit_id:)
Scott Hodgin
benar-benar menyukai solusi ini karena saya tidak perlu melakukan perubahan apa pun pada pemicunya
srh
18

Untuk mengatasi masalah Anda, kami harus mengambil pendekatan terprogram untuk masalah tersebut. Ada dua rute yang bisa Anda tuju di sini. Alasan untuk memerlukan pendekatan ini adalah karena Anda tidak dapat menonaktifkan pemicu untuk pernyataan tertentu, itu hanya dapat dinonaktifkan untuk keseluruhan tabel.

Opsi 1: Context_Info ()

Samuel Vanga di MS SQL Tips memiliki contoh yang bagus:

USE AdventureWorks; 
GO 
-- creating the table in AdventureWorks database 
IF OBJECT_ID('dbo.Table1') IS NOT NULL 
DROP TABLE dbo.Table1 
GO 
CREATE TABLE dbo.Table1(ID INT) 
GO 
-- Creating a trigger 
CREATE TRIGGER TR_Test ON dbo.Table1 FOR INSERT,UPDATE,DELETE 
AS 
DECLARE @Cinfo VARBINARY(128) 
SELECT @Cinfo = Context_Info() 
IF @Cinfo = 0x55555 
RETURN 
PRINT 'Trigger Executed' 
-- Actual code goes here 
-- For simplicity, I did not include any code 
GO

Sekarang ketika Samuel tidak ingin ingin pelatuk dieksekusi, mereka menggunakan ini:

SET Context_Info 0x55555 
INSERT dbo.Table1 VALUES(100)

Context_Info menggunakan tampilan sistem berikut untuk mengambil informasi mengenai sesi saat ini:

  • sys.dm_exec_requests

  • sys.dm_exec_sessions

  • sys.sysprocesses

Ideologinya di sini adalah bahwa string biner yang Anda setel hanya terpapar ke sesi saat ini, jadi ketika pelatuk dijalankan selama sesi Anda, ia akan melihat ruang lingkup dan pengaturan variabel Context_infofungsi dan itu akan melompat ke bagian melarikan diri dari pelatuk sebagai gantinya.

Opsi 2: Tabel Temp

Itzik Ben-Gan memiliki solusi hebat dalam bukunya "Di dalam Microsoft SQL Server 2008 Pemrograman T-SQL: Pemrograman T-SQL" yang juga ada dalam bukunya T-SQL Querying . Masalah utama dengan context_infofungsi ini adalah overhead TempDB kecil.

Untuk merusak kejutan tetapi tidak merusak plot buku-buku (saya merasa mereka layak dibeli dan dibaca), Anda akan mengubah pelatuk Anda.

Pemicu Anda harus melakukan pemeriksaan untuk tabel sementara. Jika tabel sementara ada, pelatuk harus tahu untuk mengakhiri dan tidak melakukan tindakan.

Dalam pernyataan pembaruan yang ingin Anda lakukan, buat tabel sementara terlebih dahulu. Itu akan terlihat dalam transaksi yang sama sebagai pemicu dan itu akan menyebabkan pemicu mengabaikan pernyataan Anda.

Contoh pemicu:

CREATE TRIGGER TRIGGERNAME ON TABLENAME for INSERT AS

IF OBJECT_ID('tempdb..#FAKETEMPTABLE') IS NOT NULL RETURN;
GO

Contoh pernyataan awal saat Anda tidak ingin pemicu dijalankan:

CREATE TABLE #FAKETEMPTABLE(col1 SMALLINT);

Taruh semuanya sebagai contoh Anda:

ALTER TRIGGER tiu_benefit ON benefit FOR 
... 
AS
...
IF OBJECT_ID('tempdb..#FAKETEMPTABLE') IS NOT NULL RETURN;
--... rest of code here
GO

CREATE TABLE #FAKETEMPTABLE(col1 SMALLINT);
UPDATE benefit SET editor = 'srh' where benefit_id = 9876;
GO
Shaulinator
sumber
2
Saya akan menggunakan context_info () alih-alih tabel temp di pemicu. Dengan kata lain, jika pemicu mendeteksi context_info mengembalikan nilai tertentu, pemicu akan berfungsi sesuai. Anda dapat merujuk pertanyaan SO yang relevan di sini: stackoverflow.com/questions/3025662/...
jyao
1
Anda juga bisa memasukkan cek yang mirip dengan context_infomenggunakan original_login()untuk memberi tahu pemicu untuk tidak pernah berjalan jika orang tertentu memukul pelatuk.
Kenneth Fisher
2

Saya akan menggunakan salah satu CONTEXT_INFOatau yang lebih baru SESSION_CONTEXT. Keduanya adalah nilai berbasis sesi.

  • CONTEXT_INFOadalah VARBINARY(128)nilai tunggal . Ini telah tersedia sejak setidaknya SQL Server 2000. CONTEXT_INFOdapat dilihat oleh siapa saja VIEW SERVER STATEkarena merupakan bidang yang dikembalikan oleh sys.dm_exec_sessionsDMV. Saya telah menggunakan yang ini sebelumnya dan bekerja dengan sangat baik.

    Set via SET CONTEXT_INFO
    Dapatkan melalui CONTEXT_INFO () atau sys.dm_exec_sessions

    Tergantung pada jenis nilai yang Anda simpan CONTEXT_INFO, ada beberapa nuansa yang harus diperhatikan. Saya membahasnya dalam posting blog berikut:

    Mengapa Tidak CONTEXT_INFO () Mengembalikan Nilai Tepat yang Ditetapkan oleh SET CONTEXT_INFO?

  • Session_context adalah pasangan kunci / nilai SQL_VARIANTnilai. Ini diperkenalkan di SQL Server 2016. Pemisahan nilai untuk tujuan yang berbeda cukup bagus. Session_context hanya dapat dilihat oleh sesi saat ini.

    Tetapkan nilai ini melalui sp_set_session_context
    Dapatkan nilai ini melalui SESSION_CONTEXT

Satu hal yang perlu dipertimbangkan mengenai opsi tabel sementara lokal dan bahkan opsi nonaktifkan / aktifkan Pemicu: keduanya memerlukan sejumlah aktivitas penguncian dan tran log. Kedua opsi tersebut meningkatkan potensi pertikaian, meskipun minimal. Dua opsi "konteks" harus lebih ringan / hanya memori.

Solomon Rutzky
sumber
context_info adalah penghilang rasa sakit, setiap kali Anda ingin menjalankan perubahan data produksi ini berguna, terutama menonaktifkan pemicu dapat menyebabkan operasi lain tidak memecat pemicu.
Biju jose