检查 SQL Server 中是不是存在触发器的最便携方法是啥?

Posted

技术标签:

【中文标题】检查 SQL Server 中是不是存在触发器的最便携方法是啥?【英文标题】:What is the most portable way to check whether a trigger exists in SQL Server?检查 SQL Server 中是否存在触发器的最便携方法是什么? 【发布时间】:2010-10-12 19:05:13 【问题描述】:

我正在寻找最便携的方法来检查 MS SQL Server 中是否存在触发器。它至少需要在 SQL Server 2000、2005 和 2008 上运行。

该信息似乎不在 INFORMATION_SCHEMA 中,但如果它在某处,我更愿意从那里使用它。

我知道这种方法:

if exists (
    select * from dbo.sysobjects 
    where name = 'MyTrigger' 
    and OBJECTPROPERTY(id, 'IsTrigger') = 1
) 
begin

end

但我不确定它是否适用于所有 SQL Server 版本。

【问题讨论】:

【参考方案1】:

Sql Server Management Studio 生成:

IF  EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[RolesYAccesos2016_UsuariosCRM_trgAfterInsert]'))
DROP TRIGGER [dbo].[RolesYAccesos2016_UsuariosCRM_trgAfterInsert]
GO


CREATE TRIGGER [dbo].[RolesYAccesos2016_UsuariosCRM_trgAfterInsert] 
ON  [PortalMediadores].[dbo].[RolesYAccesos2016.UsuariosCRM]
FOR INSERT
AS  
...

对于select @@version

Microsoft SQL Server 2008 R2 (RTM) - 10.50.1797.0 (X64) 2011 年 6 月 1 日 15:43:18 版权所有 (c) Microsoft Corporation Enterprise Edition Windows NT 6.1(内部版本 7601:Service Pack 1)上的(64 位) (管理程序)

【讨论】:

【参考方案2】:

这适用于 SQL Server 2000 及更高版本

IF OBJECTPROPERTY(OBJECT_ID('your_trigger'), 'IsTrigger') = 1
BEGIN
    ...
END

请注意,幼稚的逆向不能可靠地工作:

-- This doesn't work for checking for absense
IF OBJECTPROPERTY(OBJECT_ID('your_trigger'), 'IsTrigger') <> 1
BEGIN
    ...
END

...因为如果对象根本不存在,OBJECTPROPERTY 返回NULL,而NULL(当然)不是&lt;&gt; 1(或其他任何东西)。

在 SQL Server 2005 或更高版本上,您可以使用 COALESCE 来处理该问题,但如果您需要支持 SQL Server 2000,则必须构建语句来处理三个可能的返回值:@987654329 @(对象根本不存在)、0(存在但不是触发器)或1(是触发器)。

【讨论】:

Important Caveat: "不能使用 OBJECT_ID 查询非架构范围的对象,例如 DDL 触发器。" 在这些情况下,解决方法是使用@marc_s 在他的回答中描述的sys.triggers 视图。【参考方案3】:

如果您尝试在 SQL Server 2014 上查找服务器范围的 DDL 触发器,您应该尝试 sys.server_triggers。

IF EXISTS (SELECT * FROM sys.server_triggers WHERE name = 'your trigger name')
BEGIN
    do whatever you want here
END

如果我告诉你任何不正确的地方,请告诉我。

编辑: 我没有在其他版本的 SQL Server 上检查这个 dm。

【讨论】:

【参考方案4】:

我会使用这种语法来检查和删除触发器

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[SCHEMA_NAME].[TRIGGER_NAME]') AND type in (N'TR'))
DROP TRIGGER [SCHEMA_NAME].[TRIGGER_NAME]

【讨论】:

【参考方案5】:

除了marc_s的出色回答:

如果存在性检查旨在以某种方式删除或修改触发器之前,请使用直接 TSQL try/Catch bock,作为最快的方法。

例如:

BEGIN TRY
    DROP TRIGGER MyTableAfterUpdate;
END TRY
BEGIN CATCH
    SELECT ERROR_NUMBER() AS erno WHERE erno = 3701; -- may differ in SQL Server < 2005
END CATCH;

错误信息将是

Cannot drop the trigger 'MyTableAfterUpdate', because it does not exist or you do not have permission.

然后只需检查执行结果是否返回行,这在直接 sql 以及编程 API(C#,...)中很容易。

【讨论】:

【参考方案6】:

经过测试,不能在 SQL Server 2000 上运行:

select * from sys.triggers where name = 'MyTrigger'

在 SQL Server 2000 和 SQL Server 2005 上测试并正常工作:

select * from dbo.sysobjects
where name = 'MyTrigger' and OBJECTPROPERTY(id, 'IsTrigger')

【讨论】:

FWIW,您的 select * from dbo.sysobjects where name = 'MyTrigger' and OBJECTPROPERTY(id, 'IsTrigger') 不适用于我在 SQL-2000 上正常运行的 after insert, delete 触发器。该行在sysobjects 中,但其ID 上的OBJECTPROPERTY(id, 'IsTrigger')(作为上述的一部分或单独使用其原始ID)给出0。检查xtype = 'TR'type = 'TR' 有效。【参考方案7】:

触发器名称在 SQL Server 中是否强制唯一?

由于触发器根据定义应用于特定表,因此将搜索限制在相关表中不是更有效吗?

我们有一个数据库,其中包含超过 30k 个表,所有这些表都至少有一个触发器,并且可能有更多(糟糕的数据库设计 - 很可能,但它在几年前是有意义的,并且不能很好地扩展)

我用

SELECT * FROM sys.triggers 
WHERE [parent_id] = OBJECT_ID(@tableName) 
AND [name] = @triggerName

【讨论】:

【参考方案8】:

假设它是一个 DML 触发器:

IF OBJECT_ID('your_trigger', 'TR') IS NOT NULL
BEGIN
    PRINT 'Trigger exists'
END
ELSE
BEGIN
    PRINT 'Trigger does not exist'
END

对于其他类型的对象(表、视图、键等),请参阅:http://msdn.microsoft.com/en-us/library/ms190324.aspx under 'type'。

【讨论】:

这会检查任何对象类型,而不仅仅是触发器。 它打印'触发器存在',适用于 sql server 2012【参考方案9】:

还有首选的“sys.triggers”目录视图:

select * from sys.triggers where name = 'MyTrigger'

或调用 sp_Helptrigger 存储过程:

exec sp_helptrigger 'MyTableName'

但除此之外,我想就是这样:-)

马克

更新(针对 Jakub Januszkiewicz):

如果您需要包含架构信息,您还可以执行以下操作:

SELECT
    (list of columns)
FROM sys.triggers tr
INNER JOIN sys.tables t ON tr.parent_id = t.object_id
WHERE t.schema_id = SCHEMA_ID('dbo')   -- or whatever you need

【讨论】:

select * from sys.triggers where name = 'MyTrigger' 不适用于我的(正常工作等)触发器,而 wgw 的 IF OBJECTPROPERTY(OBJECT_ID('your_trigger'), 'IsTrigger') = 1 则... MSSQL 2008 R2。 @JakubJanuszkiewicz:当你运行这个时,你在正确的数据库中吗? sys.triggers 始终显示当前数据库中的触发器 - 它不会显示来自所有数据库的所有触发器... @marc-s:我在正确的数据库中。我发现了问题 - sys.triggers 中的 name 列只是一个名称(没有架构名称),而 OBJECT_ID('...') 需要一个架构限定名称(至少如果架构是非默认名称,如果我理解正确)。所以在我将我的工作OBJECT_ID('MySchema.MyTrigger') 复制到select * from sys.triggers 之后,它没有工作。仅通过“MyTrigger”过滤即可。 @marc-s:顺便说一句,这也意味着如果您在数据库的不同模式中有多个同名触发器,select * from sys.triggers 会给您一个误报。类似于select * from sys.objects where type = 'TR' and schema_id = (select schema_id from sys.schemas where name = 'YourSchema') and name = 'YourTrigger' 的东西会得到正确的触发。 这在 SQL Server 2000 中不起作用,正如问题所要求的那样,因为系统...目录视图是在 SQL Server 2005 中引入的。

以上是关于检查 SQL Server 中是不是存在触发器的最便携方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何检查Sql server中是不是存在约束?

如何检查 SQL Server 中是不是存在列?

检查 SQL Server 登录是不是已存在

Sql Server 2008如何检查表中是不是存在列? [复制]

检查表是不是存在sql server [重复]

如何检查 SQL Server CE 3.5 中是不是存在表