`

禁用约束和触发器禁用约束和触发器

 
阅读更多

禁用约束和触发器

分类: Sql server 152人阅读 评论(1) 收藏 举报

约束是在数据库中维护数据一致性的重要工具。但是,有时禁用一个或多个约束来执行某些任务(例如,使用脱机数据库进行数据同步)会更方便。在使用 SQL Server? 复制技术同步数据库之间的数据时,可以告知单个对象在复制过程中不强制约束。
例如,在使用 NOT FOR REPLICATION 语句定义外键约束时,SQL Server 在复制过程中将不会强制约束。实际上,NOT FOR REPLICATION 语句可以直接用于定义外键约束、检查约束、标识和触发器的 T-SQL 语句中。对于使用 SQL Server 复制的操作,在适当的对象上使用 NOT FOR REPLICATION 语句是一种不错的选择。但是,如果您要手动执行数据同步,则另一个方法是手动禁用约束和触发器。
通常在需要同步数据子集以及需要更好地控制数据同步方法的联机/脱机应用程序中执行手动同步。在本月的专栏中,我将讨论何时手动禁用和启用约束会更有利,此方法可以帮助您解决哪些类型的问题以及一些解决问题的技巧。


禁用外键
我不建议从关系数据库中删除外键约束。但是,有时(例如在一系列表上执行大量的插入和更新操作以及需要更准确的结果和更佳的性能)您可能需要临时减少对一个或多个外键的引用完整性检查。当然,您只能在以正常的关系顺序无法对整个数据库执行大批量的数据更新时使用此方法。
因此何时才应禁用外键约束呢?假设您的关系数据结构有许多表,所有表都通过外键约束以某种方式彼此相关。与此数据库交互的应用程序具有一个脱机/移动版本,该版本与可能驻留在便携式计算机上的数据库的第二个实例进行通信。对主数据库所做的数据更改可能需要与脱机/移动数据库同步,而同步数据的方法有好几种。
同步数据的一个方法是将插入、更新和删除操作应用于脱机/移动数据库,以便与关系结构相一致。例如,在客户相应的订单前面插入客户,并在相应的订单详细信息前面插入订单。记录删除将按相反的方向进行(从子记录到父记录)。但是,在大型数据库结构上应用此方法时,可能因为太复杂而无法实现和维护。
另一个方法是删除外键约束,之后同步数据,然后重新创建外键约束。此方法只需进行比较小的改动(只需禁用外键约束)即可正常运行。禁用外键约束之后,可以同步数据,然后可以再次启用外键。禁用外键的语法如下所示: 复制代码
-- Disable foreign key constraint
ALTER TABLE Orders
NOCHECK CONSTRAINT
FK_Orders_Customers

-- Add a new Order record for a non-existent customer
INSERT INTO Orders (CustomerID) VALUES ('BLAH')

-- Select all orders for the non-existent customer
SELECT * FROM Orders WHERE CustomerID = 'BLAH'

此外键强制“订单”表中的 CustomerID 必须是“客户”表中的有效 CustomerID。代码会禁用外键然后将订单插入到“订单”表。插入的订单记录的 CustomerID 在父“客户”表中不存在。由于外键已禁用,完整性检查会被忽略,订单记录将成功插入。
以下代码显示了重新启用外键约束然后测试外键约束工作是否正常的方法。执行此代码时,由于强制约束,订单并未被插入。系统将返回错误消息,表明插入语句与外键约束出现冲突。 复制代码
-- Enable foreign key constraint
ALTER TABLE Orders
CHECK CONSTRAINT
FK_Orders_Customers

-- Add a new Order record for a non-existent customer
INSERT INTO Orders (CustomerID) VALUES ('BLEH')

在禁用外键、触发器和其他约束时,一定要确保在该时间段内不会在数据库上执行任何数据操作语言 (DML) 操作。这必须在手动执行禁用选项时进行处理。如果您使用 SQL Server 复制和 NOT FOR REPLICATION 语句同步数据,将会自动处理该条件。


禁用触发器
也可以在需要的时候禁用触发器。有时您并不想在同步数据时激发触发器。例如,如果您更新脱机/移动数据库(如上例所示)时,您可能没有注意到几个表中可能有触发器。在将行插入触发器所在的表从而导致该行被再次插入另一个表时,可能会激发这些触发器。但是在大型数据同步中,您可能并不希望再次插入这些类型的插入。为了避免出现这种不确定的事情,您可以临时禁用触发器,如下所示: 复制代码
DISABLE TRIGGER MyTriggerName ON MyTableName

与之相反,若要启用触发器,请执行以下代码: 复制代码
ENABLE TRIGGER MyTriggerName ON MyTableName

以下代码显示了当在“区域”表中插入或更新一行或多行时会激发的触发器(并显示一条信息)。 复制代码
CREATE TRIGGER trRegion_InsertUpdate ON Region
FOR INSERT, UPDATE
AS
PRINT ' Trigger is running. '
+ CAST(@@ROWCOUNT AS VARCHAR(10))
+ ' row(s) affected.'
GO

您可以通过将一个新区域插入到“区域”表中测试此触发器,如下所示: 复制代码
INSERT INTO Region (RegionID, RegionDescription)
VALUES (5, 'Some New Region')

在查询窗口中执行此代码时,新区域将被插入“区域”表中,然后触发器会被激发,消息窗格中将显示一条消息。若要禁用触发器,您可以执行以下代码: 复制代码
DISABLE TRIGGER trRegion_InsertUpdate ON Region

那么,如果您执行此代码来更新新区域,则数据会被更新而不会激发触发器: 复制代码
UPDATE Region SET RegionDescription = 'A Great Region'
WHERE RegionID = 5

若要重新启用触发器,只需执行以下查询: 复制代码
ENABLE TRIGGER trRegion_InsertUpdate ON Region

同步脱机/移动数据库
禁用单个触发器或外键会很有用,但是在我介绍的脱机/移动数据库同步方案中,一次禁用所有触发器或外键将非常有用。若要执行此操作,您应执行以下步骤:禁用所有外键约束,禁用所有触发器,执行插入、更新和删除操作,启用所有外键约束,并最终重新启用所有触发器。
通过禁用触发器和外键约束,已经修改了数据的订单会变得不一致。在小型数据库(例如,罗斯文数据库)中,这种节省可能微不足道;但是在具有数十个或数百个表和关系的大型数据库中,将会节省大量时间。此外,如果没有此方法,则每次数据库架构添加新表和新关系时,您将不得不在脚本中修改表的顺序以便插入/更新/删除数据。禁用外键约束和触发器之后,由于顺序不再重要,因此您只需将表添加到脚本末尾即可。
请注意最后启用触发器和外键很重要,而不管脚本是否成功。例如,如果您的脚本禁用约束和触发器,并尝试修改数据,但未成功;您仍希望最后启用约束和触发器。


光标和信息架构视图
若要禁用所有外键,必须首先收集外键以及每个外键适用的表的列表。这里,您可以借助于光标和 INFORMATION_SCHEMA 视图。名为 INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS 的视图将返回当前数据库中的外键列表。若要获得外键适用的表的名称,请使用名为 INFORMATION_SCHEMA.TABLE_CONSTRAINTS 的视图。通过将以下代码加入这两个视图可以返回所有外键及其相应表的列表。 复制代码
SELECT
ref.constraint_name AS FK_Name,
fk.table_name AS FK_Table
FROM
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ref
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk
ON ref.constraint_name = fk.constraint_name
ORDER BY
fk.table_name,
ref.constraint_name

禁用所有外键
有了这个列表后,您可以编写脚本来禁用外键。您可以声明并打开光标,循环访问该列表,然后创建用于禁用外键的 T-SQL 命令,并针对每个外键执行该命令(请参见图 1)。
Figure 1 禁用和启用所有外键
复制代码
CREATE PROCEDURE pr_Disable_Triggers_v2
@disable BIT = 1
AS
DECLARE
@sql VARCHAR(500),
@tableName VARCHAR(128),
@tableSchema VARCHAR(128)

-- List of all tables
DECLARE triggerCursor CURSOR
FOR
SELECT
t.TABLE_NAME AS TableName,
t.TABLE_SCHEMA AS TableSchema
FROM
INFORMATION_SCHEMA.TABLES t
ORDER BY
t.TABLE_NAME,
t.TABLE_SCHEMA

OPEN triggerCursor

FETCH NEXT FROM triggerCursor
INTO @tableName, @tableSchema

WHILE ( @@FETCH_STATUS = 0 )
BEGIN
IF @disable = 1
SET @sql = ‘ALTER TABLE ‘ + @tableSchema
+ ‘.[‘ + @tableName + ‘] DISABLE TRIGGER ALL’
ELSE
SET @sql = ‘ALTER TABLE ‘ + @tableSchema
+ ‘.[‘ + @tableName + ‘] ENABLE TRIGGER ALL’

PRINT ‘Executing Statement - ‘ + @sql

EXECUTE ( @sql )
FETCH NEXT FROM triggerCursor
INTO @tableName, @tableSchema
END

CLOSE triggerCursor
DEALLOCATE triggerCursor

首先,foreignKeyCursor 光标被声明为收集外键及其表名称列表的 SELECT 语句。接下来,打开光标并执行初始 FETCH 语句。此 FETCH 语句将第一行的数据读取到局部变量 @foreignKeyName 和 @tableName。
在循环光标时,您可以看到 @@FETCH_STATUS 的值为 0,这表示提取成功。这意味着循环将会继续,因此可以从行集中获取各个后续外键。
连接上的所有光标都可以使用 @@FETCH_STATUS。因此,如果您正在循环多个光标,一定要检查紧跟着 FETCH 语句的语句中 @@FETCH_STATUS 的值。@@FETCH_STATUS 将反映连接中最近的 FETCH 操作的状态。@@FETCH_STATUS 的有效值包括:
0 = FETCH 成功
-1 = FETCH 失败
-2 = 提取的行丢失
在循环内,代码会根据不同意图(禁用还是启用外键约束)来构建不同的 ALTER TABLE 命令(使用 CHECK 或 NOCHECK 关键字)。接下来,语句将被打印成一条消息(这样可以看到其进度),然后执行该语句。最后在循环处理所有行之后,存储过程关闭并释放光标。


禁用所有触发器
图 1 中的存储过程将禁用或启用数据库中的所有外键。在某些情况下,您可能希望在数据同步过程中禁用所有触发器。图 2 中的 pr_Disable_Triggers 存储过程正好可以完成该操作。
Figure 2 禁用和启用所有触发器
复制代码
CREATE PROCEDURE pr_Disable_Triggers
@disable BIT = 1
AS
DECLARE
@sql VARCHAR(500),
@tableName VARCHAR(128),
@triggerName VARCHAR(128),
@tableSchema VARCHAR(128)

-- List of all triggers and tables that exist on them
DECLARE triggerCursor CURSOR
FOR
SELECT
so_tr.name AS TriggerName,
so_tbl.name AS TableName,
t.TABLE_SCHEMA AS TableSchema
FROM
sysobjects so_tr
INNER JOIN sysobjects so_tbl ON so_tr.parent_obj = so_tbl.id
INNER JOIN INFORMATION_SCHEMA.TABLES t
ON
t.TABLE_NAME = so_tbl.name
WHERE
so_tr.type = ‘TR’
ORDER BY
so_tbl.name ASC,
so_tr.name ASC

OPEN triggerCursor

FETCH NEXT FROM triggerCursor
INTO @triggerName, @tableName, @tableSchema

WHILE ( @@FETCH_STATUS = 0 )
BEGIN
IF @disable = 1
SET @sql = ‘DISABLE TRIGGER [‘
+ @triggerName + ‘] ON ‘
+ @tableSchema + ‘.[‘ + @tableName + ‘]’
ELSE
SET @sql = ‘ENABLE TRIGGER [‘
+ @triggerName + ‘] ON ‘
+ @tableSchema + ‘.[‘ + @tableName + ‘]’

PRINT ‘Executing Statement - ‘ + @sql
EXECUTE ( @sql )
FETCH NEXT FROM triggerCursor
INTO @triggerName, @tableName, @tableSchema
END

CLOSE triggerCursor
DEALLOCATE triggerCursor

pr_Disable_Triggers 存储过程将获取一个包含当前数据库中所有触发器及其相应表名称(和架构)的行集。由于没有 INFORMATION_SCHEMA.TRIGGERS 视图,我将从 SQL Server 2005 系统表和 INFORMATION_SCHEMA 视图中收集触发器列表和相关信息(如果可能)。
sysobjects 系统表位于所有数据库中,可以通过查询来返回所有触发器或表的列表。此系统表本身也可以加入以获取要操作的触发器和表列表,如图 2 中的光标声明所示。
如果在数据库上使用诸如 AdventureWorks 的存储过程(表在特定的架构中),您必须将架构名称作为表名称的前缀。pr_Disable_Triggers 存储过程通过加入 INFORMATION_SCHEMA.TABLES 视图来处理此情况,该视图返回表的 SCHEMA_NAME。
编写要禁用和重新启用触发器和外键的例程后,您可以在脚本中使用这些例程(该脚本用于修改脱机/移动数据库以使其与主数据库保持同步)。例如,您可以使用以下脚本(将中间的注释替换为数据操作运算)。 复制代码
pr_Disable_Foreign_Keys 1
pr_Disable_Triggers 1
-- Perform data operations
pr_Disable_Foreign_Keys 0
pr_Disable_Triggers 0

迅速禁用所有触发器
您可以修改图 2 中显示的 pr_Disable_Triggers 存储过程来执行与前面稍有不同的 T-SQL 命令(该命令将启用或禁用所有触发器)。其中的 T-SQL 语句将使用 ALTER TABLE 命令(禁用或启用一个表中的所有触发器)的风格。通过使用该方法,您可以修改 pr_Disable_Triggers 存储过程,针对每个表执行以下语句来禁用该表上的所有触发器: 复制代码
ALTER TABLE MySchemaName.MyTableName DISABLE TRIGGER ALL

通过使用该语法,光标的查询将收集全部表名称,而无需收集触发器本身的名称。因此,可以通过修改存储过程来使用该方法遍历表列表并启用或禁用每个表上的所有触发器。修改后的存储过程如图 3 所示。
Figure 3 使用 ALTER TABLE 命令禁用所有触发器
复制代码
CREATE PROCEDURE pr_Disable_Foreign_Keys
@disable BIT = 1
AS
DECLARE
@sql VARCHAR(500),
@tableName VARCHAR(128),
@foreignKeyName VARCHAR(128)

-- A list of all foreign keys and table names
DECLARE foreignKeyCursor CURSOR
FOR SELECT
ref.constraint_name AS FK_Name,
fk.table_name AS FK_Table
FROM
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ref
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk
ON ref.constraint_name = fk.constraint_name
ORDER BY
fk.table_name,
ref.constraint_name

OPEN foreignKeyCursor

FETCH NEXT FROM foreignKeyCursor
INTO @foreignKeyName, @tableName

WHILE ( @@FETCH_STATUS = 0 )
BEGIN
IF @disable = 1
SET @sql = ‘ALTER TABLE [‘
+ @tableName + ‘] NOCHECK CONSTRAINT [‘
+ @foreignKeyName + ‘]’
ELSE
SET @sql = ‘ALTER TABLE [‘
+ @tableName + ‘] CHECK CONSTRAINT [‘
+ @foreignKeyName + ‘]’

PRINT ‘Executing Statement - ‘ + @sql

EXECUTE(@sql)
FETCH NEXT FROM foreignKeyCursor
INTO @foreignKeyName, @tableName
END

CLOSE foreignKeyCursor
DEALLOCATE foreignKeyCursor

结束语
与从 .NET 应用程序逐一执行各个数据库操作相比,从 T-SQL 脚本执行多个数据库操作(如本文所述)会更高效。例如,您可以使用 ADO.NET 收集 .NET 应用程序的外键列表,然后执行相应的命令来禁用各个外键。同样可以将其扩展为获取触发器列表,然后禁用触发器。所有这些操作都需要在 .NET 代码和数据库之间来回进行操作,这与完全在 T-SQL 的数据库服务器上执行代码相比会占用更多的资源。

约束是在数据库中维护数据一致性的重要工具。但是,有时禁用一个或多个约束来执行某些任务(例如,使用脱机数据库进行数据同步)会更方便。在使用 SQL Server? 复制技术同步数据库之间的数据时,可以告知单个对象在复制过程中不强制约束。
例如,在使用 NOT FOR REPLICATION 语句定义外键约束时,SQL Server 在复制过程中将不会强制约束。实际上,NOT FOR REPLICATION 语句可以直接用于定义外键约束、检查约束、标识和触发器的 T-SQL 语句中。对于使用 SQL Server 复制的操作,在适当的对象上使用 NOT FOR REPLICATION 语句是一种不错的选择。但是,如果您要手动执行数据同步,则另一个方法是手动禁用约束和触发器。
通常在需要同步数据子集以及需要更好地控制数据同步方法的联机/脱机应用程序中执行手动同步。在本月的专栏中,我将讨论何时手动禁用和启用约束会更有利,此方法可以帮助您解决哪些类型的问题以及一些解决问题的技巧。


禁用外键
我不建议从关系数据库中删除外键约束。但是,有时(例如在一系列表上执行大量的插入和更新操作以及需要更准确的结果和更佳的性能)您可能需要临时减少对一个或多个外键的引用完整性检查。当然,您只能在以正常的关系顺序无法对整个数据库执行大批量的数据更新时使用此方法。
因此何时才应禁用外键约束呢?假设您的关系数据结构有许多表,所有表都通过外键约束以某种方式彼此相关。与此数据库交互的应用程序具有一个脱机/移动版本,该版本与可能驻留在便携式计算机上的数据库的第二个实例进行通信。对主数据库所做的数据更改可能需要与脱机/移动数据库同步,而同步数据的方法有好几种。
同步数据的一个方法是将插入、更新和删除操作应用于脱机/移动数据库,以便与关系结构相一致。例如,在客户相应的订单前面插入客户,并在相应的订单详细信息前面插入订单。记录删除将按相反的方向进行(从子记录到父记录)。但是,在大型数据库结构上应用此方法时,可能因为太复杂而无法实现和维护。
另一个方法是删除外键约束,之后同步数据,然后重新创建外键约束。此方法只需进行比较小的改动(只需禁用外键约束)即可正常运行。禁用外键约束之后,可以同步数据,然后可以再次启用外键。禁用外键的语法如下所示: 复制代码
-- Disable foreign key constraint
ALTER TABLE Orders
NOCHECK CONSTRAINT
FK_Orders_Customers

-- Add a new Order record for a non-existent customer
INSERT INTO Orders (CustomerID) VALUES ('BLAH')

-- Select all orders for the non-existent customer
SELECT * FROM Orders WHERE CustomerID = 'BLAH'

此外键强制“订单”表中的 CustomerID 必须是“客户”表中的有效 CustomerID。代码会禁用外键然后将订单插入到“订单”表。插入的订单记录的 CustomerID 在父“客户”表中不存在。由于外键已禁用,完整性检查会被忽略,订单记录将成功插入。
以下代码显示了重新启用外键约束然后测试外键约束工作是否正常的方法。执行此代码时,由于强制约束,订单并未被插入。系统将返回错误消息,表明插入语句与外键约束出现冲突。 复制代码
-- Enable foreign key constraint
ALTER TABLE Orders
CHECK CONSTRAINT
FK_Orders_Customers

-- Add a new Order record for a non-existent customer
INSERT INTO Orders (CustomerID) VALUES ('BLEH')

在禁用外键、触发器和其他约束时,一定要确保在该时间段内不会在数据库上执行任何数据操作语言 (DML) 操作。这必须在手动执行禁用选项时进行处理。如果您使用 SQL Server 复制和 NOT FOR REPLICATION 语句同步数据,将会自动处理该条件。


禁用触发器
也可以在需要的时候禁用触发器。有时您并不想在同步数据时激发触发器。例如,如果您更新脱机/移动数据库(如上例所示)时,您可能没有注意到几个表中可能有触发器。在将行插入触发器所在的表从而导致该行被再次插入另一个表时,可能会激发这些触发器。但是在大型数据同步中,您可能并不希望再次插入这些类型的插入。为了避免出现这种不确定的事情,您可以临时禁用触发器,如下所示: 复制代码
DISABLE TRIGGER MyTriggerName ON MyTableName

与之相反,若要启用触发器,请执行以下代码: 复制代码
ENABLE TRIGGER MyTriggerName ON MyTableName

以下代码显示了当在“区域”表中插入或更新一行或多行时会激发的触发器(并显示一条信息)。 复制代码
CREATE TRIGGER trRegion_InsertUpdate ON Region
FOR INSERT, UPDATE
AS
PRINT ' Trigger is running. '
+ CAST(@@ROWCOUNT AS VARCHAR(10))
+ ' row(s) affected.'
GO

您可以通过将一个新区域插入到“区域”表中测试此触发器,如下所示: 复制代码
INSERT INTO Region (RegionID, RegionDescription)
VALUES (5, 'Some New Region')

在查询窗口中执行此代码时,新区域将被插入“区域”表中,然后触发器会被激发,消息窗格中将显示一条消息。若要禁用触发器,您可以执行以下代码: 复制代码
DISABLE TRIGGER trRegion_InsertUpdate ON Region

那么,如果您执行此代码来更新新区域,则数据会被更新而不会激发触发器: 复制代码
UPDATE Region SET RegionDescription = 'A Great Region'
WHERE RegionID = 5

若要重新启用触发器,只需执行以下查询: 复制代码
ENABLE TRIGGER trRegion_InsertUpdate ON Region

同步脱机/移动数据库
禁用单个触发器或外键会很有用,但是在我介绍的脱机/移动数据库同步方案中,一次禁用所有触发器或外键将非常有用。若要执行此操作,您应执行以下步骤:禁用所有外键约束,禁用所有触发器,执行插入、更新和删除操作,启用所有外键约束,并最终重新启用所有触发器。
通过禁用触发器和外键约束,已经修改了数据的订单会变得不一致。在小型数据库(例如,罗斯文数据库)中,这种节省可能微不足道;但是在具有数十个或数百个表和关系的大型数据库中,将会节省大量时间。此外,如果没有此方法,则每次数据库架构添加新表和新关系时,您将不得不在脚本中修改表的顺序以便插入/更新/删除数据。禁用外键约束和触发器之后,由于顺序不再重要,因此您只需将表添加到脚本末尾即可。
请注意最后启用触发器和外键很重要,而不管脚本是否成功。例如,如果您的脚本禁用约束和触发器,并尝试修改数据,但未成功;您仍希望最后启用约束和触发器。


光标和信息架构视图
若要禁用所有外键,必须首先收集外键以及每个外键适用的表的列表。这里,您可以借助于光标和 INFORMATION_SCHEMA 视图。名为 INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS 的视图将返回当前数据库中的外键列表。若要获得外键适用的表的名称,请使用名为 INFORMATION_SCHEMA.TABLE_CONSTRAINTS 的视图。通过将以下代码加入这两个视图可以返回所有外键及其相应表的列表。 复制代码
SELECT
ref.constraint_name AS FK_Name,
fk.table_name AS FK_Table
FROM
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ref
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk
ON ref.constraint_name = fk.constraint_name
ORDER BY
fk.table_name,
ref.constraint_name

禁用所有外键
有了这个列表后,您可以编写脚本来禁用外键。您可以声明并打开光标,循环访问该列表,然后创建用于禁用外键的 T-SQL 命令,并针对每个外键执行该命令(请参见图 1)。
Figure 1 禁用和启用所有外键
复制代码
CREATE PROCEDURE pr_Disable_Triggers_v2
@disable BIT = 1
AS
DECLARE
@sql VARCHAR(500),
@tableName VARCHAR(128),
@tableSchema VARCHAR(128)

-- List of all tables
DECLARE triggerCursor CURSOR
FOR
SELECT
t.TABLE_NAME AS TableName,
t.TABLE_SCHEMA AS TableSchema
FROM
INFORMATION_SCHEMA.TABLES t
ORDER BY
t.TABLE_NAME,
t.TABLE_SCHEMA

OPEN triggerCursor

FETCH NEXT FROM triggerCursor
INTO @tableName, @tableSchema

WHILE ( @@FETCH_STATUS = 0 )
BEGIN
IF @disable = 1
SET @sql = ‘ALTER TABLE ‘ + @tableSchema
+ ‘.[‘ + @tableName + ‘] DISABLE TRIGGER ALL’
ELSE
SET @sql = ‘ALTER TABLE ‘ + @tableSchema
+ ‘.[‘ + @tableName + ‘] ENABLE TRIGGER ALL’

PRINT ‘Executing Statement - ‘ + @sql

EXECUTE ( @sql )
FETCH NEXT FROM triggerCursor
INTO @tableName, @tableSchema
END

CLOSE triggerCursor
DEALLOCATE triggerCursor

首先,foreignKeyCursor 光标被声明为收集外键及其表名称列表的 SELECT 语句。接下来,打开光标并执行初始 FETCH 语句。此 FETCH 语句将第一行的数据读取到局部变量 @foreignKeyName 和 @tableName。
在循环光标时,您可以看到 @@FETCH_STATUS 的值为 0,这表示提取成功。这意味着循环将会继续,因此可以从行集中获取各个后续外键。
连接上的所有光标都可以使用 @@FETCH_STATUS。因此,如果您正在循环多个光标,一定要检查紧跟着 FETCH 语句的语句中 @@FETCH_STATUS 的值。@@FETCH_STATUS 将反映连接中最近的 FETCH 操作的状态。@@FETCH_STATUS 的有效值包括:
0 = FETCH 成功
-1 = FETCH 失败
-2 = 提取的行丢失
在循环内,代码会根据不同意图(禁用还是启用外键约束)来构建不同的 ALTER TABLE 命令(使用 CHECK 或 NOCHECK 关键字)。接下来,语句将被打印成一条消息(这样可以看到其进度),然后执行该语句。最后在循环处理所有行之后,存储过程关闭并释放光标。


禁用所有触发器
图 1 中的存储过程将禁用或启用数据库中的所有外键。在某些情况下,您可能希望在数据同步过程中禁用所有触发器。图 2 中的 pr_Disable_Triggers 存储过程正好可以完成该操作。
Figure 2 禁用和启用所有触发器
复制代码
CREATE PROCEDURE pr_Disable_Triggers
@disable BIT = 1
AS
DECLARE
@sql VARCHAR(500),
@tableName VARCHAR(128),
@triggerName VARCHAR(128),
@tableSchema VARCHAR(128)

-- List of all triggers and tables that exist on them
DECLARE triggerCursor CURSOR
FOR
SELECT
so_tr.name AS TriggerName,
so_tbl.name AS TableName,
t.TABLE_SCHEMA AS TableSchema
FROM
sysobjects so_tr
INNER JOIN sysobjects so_tbl ON so_tr.parent_obj = so_tbl.id
INNER JOIN INFORMATION_SCHEMA.TABLES t
ON
t.TABLE_NAME = so_tbl.name
WHERE
so_tr.type = ‘TR’
ORDER BY
so_tbl.name ASC,
so_tr.name ASC

OPEN triggerCursor

FETCH NEXT FROM triggerCursor
INTO @triggerName, @tableName, @tableSchema

WHILE ( @@FETCH_STATUS = 0 )
BEGIN
IF @disable = 1
SET @sql = ‘DISABLE TRIGGER [‘
+ @triggerName + ‘] ON ‘
+ @tableSchema + ‘.[‘ + @tableName + ‘]’
ELSE
SET @sql = ‘ENABLE TRIGGER [‘
+ @triggerName + ‘] ON ‘
+ @tableSchema + ‘.[‘ + @tableName + ‘]’

PRINT ‘Executing Statement - ‘ + @sql
EXECUTE ( @sql )
FETCH NEXT FROM triggerCursor
INTO @triggerName, @tableName, @tableSchema
END

CLOSE triggerCursor
DEALLOCATE triggerCursor

pr_Disable_Triggers 存储过程将获取一个包含当前数据库中所有触发器及其相应表名称(和架构)的行集。由于没有 INFORMATION_SCHEMA.TRIGGERS 视图,我将从 SQL Server 2005 系统表和 INFORMATION_SCHEMA 视图中收集触发器列表和相关信息(如果可能)。
sysobjects 系统表位于所有数据库中,可以通过查询来返回所有触发器或表的列表。此系统表本身也可以加入以获取要操作的触发器和表列表,如图 2 中的光标声明所示。
如果在数据库上使用诸如 AdventureWorks 的存储过程(表在特定的架构中),您必须将架构名称作为表名称的前缀。pr_Disable_Triggers 存储过程通过加入 INFORMATION_SCHEMA.TABLES 视图来处理此情况,该视图返回表的 SCHEMA_NAME。
编写要禁用和重新启用触发器和外键的例程后,您可以在脚本中使用这些例程(该脚本用于修改脱机/移动数据库以使其与主数据库保持同步)。例如,您可以使用以下脚本(将中间的注释替换为数据操作运算)。 复制代码
pr_Disable_Foreign_Keys 1
pr_Disable_Triggers 1
-- Perform data operations
pr_Disable_Foreign_Keys 0
pr_Disable_Triggers 0

迅速禁用所有触发器
您可以修改图 2 中显示的 pr_Disable_Triggers 存储过程来执行与前面稍有不同的 T-SQL 命令(该命令将启用或禁用所有触发器)。其中的 T-SQL 语句将使用 ALTER TABLE 命令(禁用或启用一个表中的所有触发器)的风格。通过使用该方法,您可以修改 pr_Disable_Triggers 存储过程,针对每个表执行以下语句来禁用该表上的所有触发器: 复制代码
ALTER TABLE MySchemaName.MyTableName DISABLE TRIGGER ALL

通过使用该语法,光标的查询将收集全部表名称,而无需收集触发器本身的名称。因此,可以通过修改存储过程来使用该方法遍历表列表并启用或禁用每个表上的所有触发器。修改后的存储过程如图 3 所示。
Figure 3 使用 ALTER TABLE 命令禁用所有触发器
复制代码
CREATE PROCEDURE pr_Disable_Foreign_Keys
@disable BIT = 1
AS
DECLARE
@sql VARCHAR(500),
@tableName VARCHAR(128),
@foreignKeyName VARCHAR(128)

-- A list of all foreign keys and table names
DECLARE foreignKeyCursor CURSOR
FOR SELECT
ref.constraint_name AS FK_Name,
fk.table_name AS FK_Table
FROM
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ref
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk
ON ref.constraint_name = fk.constraint_name
ORDER BY
fk.table_name,
ref.constraint_name

OPEN foreignKeyCursor

FETCH NEXT FROM foreignKeyCursor
INTO @foreignKeyName, @tableName

WHILE ( @@FETCH_STATUS = 0 )
BEGIN
IF @disable = 1
SET @sql = ‘ALTER TABLE [‘
+ @tableName + ‘] NOCHECK CONSTRAINT [‘
+ @foreignKeyName + ‘]’
ELSE
SET @sql = ‘ALTER TABLE [‘
+ @tableName + ‘] CHECK CONSTRAINT [‘
+ @foreignKeyName + ‘]’

PRINT ‘Executing Statement - ‘ + @sql

EXECUTE(@sql)
FETCH NEXT FROM foreignKeyCursor
INTO @foreignKeyName, @tableName
END

CLOSE foreignKeyCursor
DEALLOCATE foreignKeyCursor

结束语
与从 .NET 应用程序逐一执行各个数据库操作相比,从 T-SQL 脚本执行多个数据库操作(如本文所述)会更高效。例如,您可以使用 ADO.NET 收集 .NET 应用程序的外键列表,然后执行相应的命令来禁用各个外键。同样可以将其扩展为获取触发器列表,然后禁用触发器。所有这些操作都需要在 .NET 代码和数据库之间来回进行操作,这与完全在 T-SQL 的数据库服务器上执行代码相比会占用更多的资源。

分享到:
评论

相关推荐

    Mysql中禁用与启动触发器教程【推荐】

    例如,在进行大批量数据导入或维护操作时,为了避免触发器导致额外的计算和可能的错误,我们可以暂时禁用触发器。在完成后,再通过设置变量恢复其启用状态。此外,这也可以用于实现临时的逻辑调整,比如在某些特定...

    sqlserver 禁用触发器和启用触发器的语句

    - 总是在禁用触发器前后记录相关信息,以便跟踪和还原操作。 - 使用事务处理禁用和启用操作,确保操作的原子性。 - 在禁用触发器后,应尽快重新启用,以维持数据完整性和业务逻辑的正常运行。 综上所述,理解和...

    sql server语法

    - 在启用或禁用触发器时,需要考虑到它们可能影响到的其他数据库操作和依赖。 5. **最佳实践**: - 在执行大量数据修改或导入操作前,先禁用约束和触发器可以提高性能。 - 修改完成后,应立即恢复约束和触发器,...

    oracle存储过程和触发器

    在某些情况下,可能需要暂时禁用触发器,可以使用`ALTER TABLE table DISABLE TRIGGER trigger_name;`。 **4. 实例应用** 假设我们有一个需求,在删除部门时,自动删除该部门下的所有员工。可以创建以下触发器: ``...

    锁表进程和触发器开关

    这段SQL查询了特定用户(例如 DEVUSER)下名称不包含“SYN”的已禁用触发器。 #### 三、查看表空间大小 了解表空间的大小对于数据库性能管理和优化非常重要。以下SQL语句可以帮助我们了解各个表空间的使用情况。 ...

    把触发器说透

    除了创建之外,触发器的管理也非常重要,包括删除、使能和禁用触发器。触发器的存在可能会影响数据库性能,特别是在高并发环境下,过多的触发器会显著增加数据操作的时间开销。因此,合理设计和维护触发器是非常必要...

    关于SqlServer 触发器的PPT

    禁用和启用触发器则分别使用`ALTER TABLE`语句的`DISABLE TRIGGER`和`ENABLE TRIGGER`子句。 触发器的一个常见应用是在阻止特定操作的例子中,例如不允许用户更改用户名。可以创建一个更新触发器,如果检测到`...

    触发器

    这些文档可能会涵盖如复合触发器、行级和语句级触发器的差异、如何禁用和启用触发器、以及如何管理触发器冲突等内容。对于希望深入了解Oracle数据库管理和编程的开发者来说,这些都是宝贵的资源。

    基于sql server 2005的触发器的创建及操作

    触发器可以帮助实现复杂的业务规则和数据完整性,通过扩展SQL语句的功能,提供了一种在数据库层面上对数据进行操作的机制。 一、触发器的类型 SQL Server 2005中的触发器分为两种类型: 1. DML触发器:包括AFTER和...

    oracle触发器

    - **禁用触发器**:可以使用 `ALTER TRIGGER trigger_name DISABLE;` 来暂时禁用触发器。 - **启用触发器**:同样地,使用 `ALTER TRIGGER trigger_name ENABLE;` 可以重新激活已禁用的触发器。 - **禁用/启用表上的...

    SQL触发器知识和实例

    - **数据一致性**:确保数据符合特定的规则和约束。 - **审计跟踪**:记录数据更改的历史。 - **级联更新/删除**:在更新或删除主表中的数据时,自动更新或删除相关子表中的数据。 - **复杂业务逻辑的实现**:通过...

    获取SQL触发器的状态

    相反,如果触发器被禁用,则即使有触发事件发生也不会执行其代码。因此,了解触发器当前是否启用对于确保数据库行为符合预期非常重要。 #### 检测触发器状态的方法 本节提供了一种用于检测SQL Server触发器状态的...

    Oracle之管理触发器.pdf

    在设计数据库逻辑时,应优先考虑使用存储过程、函数和约束,仅在其他手段无法实现特定需求时才考虑使用触发器。 6. **参考文献** - 杨少敏,王红敏. Oracle 数据库应用简明教程. 清华大学出版社,2010 年 4 月第 ...

    实验八--使用触发器实现数据完整性.pdf

    本实验的主要目的是让学生掌握使用触发器实现数据完整性的重要性和方法,并了解触发器与约束的不同。 一、实验目的 * 掌握用触发器实现数据完整性的方法 * 掌握用触发器实现参照完整性的方法 * 理解触发器与约束的...

    创建触发器

    - 禁用触发器: ```sql DISABLE TRIGGER mytrigger ON DATABASE ``` - 启用触发器: ```sql ENABLE TRIGGER mytrigger ON DATABASE ``` 6. 数据保护与事务回滚: 在触发器中,如果需要在满足特定条件时...

    把ORACLE触发器说透

    删除触发器将永久性地移除,而禁用触发器则不会执行,但保留其定义,以便以后启用。 **8.4 触发器和数据字典** 通过查询数据字典视图,如DBA_TRIGGERS或USER_TRIGGERS,可以获取关于触发器的信息,如触发器的名称...

Global site tag (gtag.js) - Google Analytics