Close

Disable all Triggers on a Database

Sometimes it’s best not to ask why. However, if for some reason you have a number of triggers on tables within a database that you would like to temporarily disable, read on.

I came across a situation recently while automating a process to refresh test environments where this exact scenario came up.  As part of the process several scripts were run to obfuscate production data. While these ran all the UPDATE triggers were firing. Not only were the triggers adding a significant amount of time to the process, they were also updating dates and other values that we’d prefer kept their original values.

Now, as I mentioned this is not a discussion on whether this is a good database design or not, this is just how to solve this issue.

In the snippet below I use Connect-DbaInstance from dbatools to create a $svr object. If you don’t have dbatools installed you could either install dbatools, or use New-Object Microsoft.SqlServer.Management.Smo.Server. The dbatools function is essentially a wrapper around this command that adds a lot of additional checks and options.

I have also defined an array $triggers to keep track of the triggers I disable. It’s likely that you’ll want to put the environment back to how it started, so this will make sure you don’t enable any triggers that started off disabled.

Then we get to the actual work. Using the $svr object we can loop through all the tables, and then all the triggers on those tables. If a certain trigger is enabled, it is added to the $triggers array and then disabled using $tr.isenabled.  As with most (all?) changes made through SMO you then need to call the alter method ,$tr.alter(), to actually make the change on the server.

$database = ‘AdventureWorks2017’
$svr = Connect-DbaInstance -SqlInstance server1
$foreach ($tbl in $svr.databases[$database].Tables)
{
    foreach ($tr in $($tbl.Triggers | Where-Object Isenabled)) {
        $triggers += $tr | Select-Object @{l='SchemaName';e={$tbl.Schema}}, @{l='TableName';e={$tbl.name}}, @{l='TriggerName';e={$_.name}}
        $tr.isenabled = $FALSE
        $tr.alter()
    }
}

When you are ready to enable the triggers again you can use the following code. This loops through the triggers that we had previously disabled and added to our array and enables them.

foreach($tr in $triggers) {
    $trigger = $svr.Databases[$database].Tables[$tr.TableName,$tr.SchemaName].Triggers[$tr.TriggerName]
    $trigger.IsEnabled = $true
    $trigger.alter()
}

8 thoughts on “Disable all Triggers on a Database

  1. One very tiny warning – if any triggers were already disabled when this code first runs, and then you run the second phase (enable all triggers), then you’ve changed the state on the server. May want to add a first step to list out the already-existing, already-disabled triggers before you start – just to see if any are going to surprise you when you enable ’em. (Boy, have I got a set of burn marks from that.)

    1. Hey Brent,
      Thanks for the comment! In the first section when I loop through the triggers I’m only including enabled ones (`Where-Object Isenabled`), and saving those into the `$triggers` variable. Then re-enable them based off that variable. Is that what you mean, or am I missing others?
      Jess

  2. Hi.
    It looks like an elegant solution for this scenario and I like Powershell, but here the question: Why do you perform it by Powershell and not via T-SQL?
    I would imagine a simple T-SQL batch from SSMS before going through Powershell, something like the following:
    — Select Active Triggers and prepare Disable and Enable T-SQL Statement and put into Temp Table
    SELECT ‘ALTER TABLE [‘ + sc.name + ‘].[‘ + ta.name + ‘] DISABLE TRIGGER [‘ + tr.name + ‘];’ AS DisableTriggerStatement,
    ‘ALTER TABLE [‘ + sc.name + ‘].[‘ + ta.name + ‘] ENABLE TRIGGER [‘ + tr.name + ‘];’ AS EnableTriggerStatement
    INTO #Triggers
    FROM sys.triggers tr
    INNER JOIN sys.tables ta ON ta.object_id = tr.parent_id
    INNER JOIN sys.schemas sc ON sc.schema_id = ta.schema_id
    WHERE tr.is_disabled = 0;

    — Perpare Variable Script for Execution
    DECLARE @Disable_Statement nvarchar(max) = (SELECT tmp.DisableTriggerStatement + CHAR(10) AS ‘data()’
    FROM #Triggers tmp
    FOR XML PATH(”));

    — Execute SQL
    EXECUTE sys.sp_executesql @Disable_Statement;

    — Perpare Variable Script for Execution
    DECLARE @Enable_Statement nvarchar(max) = ( SELECT tmp.DisableTriggerStatement + CHAR(10) AS ‘data()’
    FROM #Triggers tmp
    FOR XML PATH(”));

    — Execute SQL
    EXECUTE sys.sp_executesql @Enable_Statement;

    DROP TABLE #Triggers;

    1. For this scenario the rest of my script was being built in PowerShell, there are for sure many ways to achieve this, including T-SQL. Thanks for reading!

  3. I would add another warning
    consider that happens if you accidentally close script window or your server restarts after your disabled triggers and started other tasks…
    You come back and don’t have log whatsoever of previously active triggers to re-enable.

    1. You are right there, great point. I should write a follow up perhaps. The approach I used in the past was to export the triggers I disabled to a json file, then read it back in.

    1. Another possible solution. I was using this in a scenario where I already had the rest of the work written in PowerShell so it fit in nicely.

Leave a Reply to Cristian Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: