检查 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
(当然)不是<> 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 中是不是存在触发器的最便携方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章