SQL脚本来改变所有的外键添加ON DELETE CASCADE

我有一个SQL 2005数据库约250表。

我想暂时启用ON DELETE CASCADE到所有的外键,这样我就可以轻松地批量删除了。

然后我想在所有的外键上closuresON DELETE CASCADE。

我知道这样做的唯一方法是使用Management Studio生成一个完整的数据库创build脚本,做一些search和replace剥离除了外键以外的任何东西,保存脚本,然后做一些更多的search和replace添加ON DELETE CASCADE。

然后我运行脚本,做我的删除,然后运行其他脚本。

有一个更简单的方法来产生这个脚本? 这种方法似乎太容易出错,我将不得不保持脚本与我们对数据库做任何其他更改保持最新,或者每次我可能需要使用它时手动重新生成它。

是一个替代选项来运行系统表上的select“为我生成”的脚本? 是否可以在一个启用和禁用ON DELETE CASCADE的系统表上运行更新?

这是一个我用于类似目的的脚本。 它不支持复合外键(它使用多个字段)。它可能需要一些调整,才能适应您的情况。 编辑:特别是它不正确处理多列外键。

select DropStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema + '].[' + ForeignKeys.ForeignTableName + '] DROP CONSTRAINT [' + ForeignKeys.ForeignKeyName + ']; ' , CreateStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema + '].[' + ForeignKeys.ForeignTableName + '] WITH CHECK ADD CONSTRAINT [' + ForeignKeys.ForeignKeyName + '] FOREIGN KEY([' + ForeignKeys.ForeignTableColumn + ']) REFERENCES [' + schema_name(sys.objects.schema_id) + '].[' + sys.objects.[name] + ']([' + sys.columns.[name] + ']) ON DELETE CASCADE; ' from sys.objects inner join sys.columns on (sys.columns.[object_id] = sys.objects.[object_id]) inner join ( select sys.foreign_keys.[name] as ForeignKeyName ,schema_name(sys.objects.schema_id) as ForeignTableSchema ,sys.objects.[name] as ForeignTableName ,sys.columns.[name] as ForeignTableColumn ,sys.foreign_keys.referenced_object_id as referenced_object_id ,sys.foreign_key_columns.referenced_column_id as referenced_column_id from sys.foreign_keys inner join sys.foreign_key_columns on (sys.foreign_key_columns.constraint_object_id = sys.foreign_keys.[object_id]) inner join sys.objects on (sys.objects.[object_id] = sys.foreign_keys.parent_object_id) inner join sys.columns on (sys.columns.[object_id] = sys.objects.[object_id]) and (sys.columns.column_id = sys.foreign_key_columns.parent_column_id) ) ForeignKeys on (ForeignKeys.referenced_object_id = sys.objects.[object_id]) and (ForeignKeys.referenced_column_id = sys.columns.column_id) where (sys.objects.[type] = 'U') and (sys.objects.[name] not in ('sysdiagrams')) 

上面的Andomar的答案是好的,但仅适用于单列外键约束。 我对它进行了一些调整,以适应多列约束:

 create function dbo.fk_columns (@constraint_object_id int) returns varchar(255) as begin declare @r varchar(255) select @r = coalesce(@r + ',', '') + c.name from sys.foreign_key_columns fkc join sys.columns c on fkc.parent_object_id = c.object_id and fkc.parent_column_id = c.column_id where fkc.constraint_object_id = @constraint_object_id return @r end select distinct DropStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema + '].[' + ForeignKeys.ForeignTableName + '] DROP CONSTRAINT [' + ForeignKeys.ForeignKeyName + '] ' , CreateStmt = 'ALTER TABLE [' + ForeignKeys.ForeignTableSchema + '].[' + ForeignKeys.ForeignTableName + '] WITH CHECK ADD CONSTRAINT [' + ForeignKeys.ForeignKeyName + '] FOREIGN KEY(' + dbo.fk_columns(constraint_object_id) + ')' + 'REFERENCES [' + schema_name(sys.objects.schema_id) + '].[' + sys.objects.[name] + '] ' + ' ON DELETE CASCADE' from sys.objects inner join sys.columns on (sys.columns.[object_id] = sys.objects.[object_id]) inner join ( select sys.foreign_keys.[name] as ForeignKeyName ,schema_name(sys.objects.schema_id) as ForeignTableSchema ,sys.objects.[name] as ForeignTableName ,sys.columns.[name] as ForeignTableColumn ,sys.foreign_keys.referenced_object_id as referenced_object_id ,sys.foreign_key_columns.referenced_column_id as referenced_column_id ,sys.foreign_keys.object_id as constraint_object_id from sys.foreign_keys inner join sys.foreign_key_columns on (sys.foreign_key_columns.constraint_object_id = sys.foreign_keys.[object_id]) inner join sys.objects on (sys.objects.[object_id] = sys.foreign_keys.parent_object_id) inner join sys.columns on (sys.columns.[object_id] = sys.objects.[object_id]) and (sys.columns.column_id = sys.foreign_key_columns.parent_column_id) -- Uncomment this if you want to include only FKs that already -- have a cascade constraint. -- where (delete_referential_action_desc = 'CASCADE' or update_referential_action_desc = 'CASCADE') ) ForeignKeys on (ForeignKeys.referenced_object_id = sys.objects.[object_id]) and (ForeignKeys.referenced_column_id = sys.columns.column_id) where (sys.objects.[type] = 'U') and (sys.objects.[name] not in ('sysdiagrams')) 

您也可以使用查询来帮助从当前拥有它的FK中删除 ON DELETE CASCADE

这仍然不能处理这两个表中的列被命名为不同的事情 – 另一个用户定义的函数将需要为此定义。

更符合标准的解决scheme

 ;WITH CTE AS ( SELECT KCU1.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME ,KCU1.TABLE_SCHEMA AS FK_SCHEMA_NAME ,KCU1.TABLE_NAME AS FK_TABLE_NAME ,KCU1.COLUMN_NAME AS FK_COLUMN_NAME ,KCU1.ORDINAL_POSITION AS FK_ORDINAL_POSITION ,KCU2.CONSTRAINT_NAME AS REFERENCED_CONSTRAINT_NAME ,KCU2.TABLE_SCHEMA AS REFERENCED_SCHEMA_NAME ,KCU2.TABLE_NAME AS REFERENCED_TABLE_NAME ,KCU2.COLUMN_NAME AS REFERENCED_COLUMN_NAME ,KCU2.ORDINAL_POSITION AS REFERENCED_ORDINAL_POSITION FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION ) SELECT FK_CONSTRAINT_NAME --,FK_SCHEMA_NAME --,FK_TABLE_NAME --,FK_COLUMN_NAME --,FK_ORDINAL_POSITION --,REFERENCED_CONSTRAINT_NAME --,REFERENCED_SCHEMA_NAME --,REFERENCED_TABLE_NAME --,REFERENCED_COLUMN_NAME --,REFERENCED_ORDINAL_POSITION , 'ALTER TABLE [' + FK_SCHEMA_NAME + ']' + '.[' + FK_TABLE_NAME + '] ' + 'DROP CONSTRAINT [' + FK_CONSTRAINT_NAME + ']; ' AS DropStmt , 'ALTER TABLE [' + FK_SCHEMA_NAME + ']' + '.[' + FK_TABLE_NAME + '] ' + + 'WITH CHECK ADD CONSTRAINT [' + FK_CONSTRAINT_NAME + '] ' + 'FOREIGN KEY([' + FK_COLUMN_NAME + ']) ' + 'REFERENCES [' + REFERENCED_SCHEMA_NAME + '].[' + REFERENCED_TABLE_NAME + ']([' + REFERENCED_COLUMN_NAME + ']) ON DELETE CASCADE; ' AS CreateStmt FROM CTE WHERE (1=1) /* AND FK_TABLE_NAME IN ( 'T_SYS_Geschossrechte' ,'T_SYS_Gebaeuderechte' ,'T_SYS_Standortrechte' ) AND REFERENCED_TABLE_NAME NOT LIKE 'T_AP_Ref_Mandant' */ ORDER BY FK_TABLE_NAME ,FK_CONSTRAINT_NAME ,FK_COLUMN_NAME ,FK_ORDINAL_POSITION ,REFERENCED_CONSTRAINT_NAME ,REFERENCED_TABLE_NAME ,REFERENCED_COLUMN_NAME ,REFERENCED_ORDINAL_POSITION 

编辑:
扩展为多列外键:

 ;WITH CTE AS ( SELECT KCU1.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME ,KCU1.TABLE_SCHEMA AS FK_SCHEMA_NAME ,KCU1.TABLE_NAME AS FK_TABLE_NAME ,KCU1.COLUMN_NAME AS FK_COLUMN_NAME ,KCU1.ORDINAL_POSITION AS FK_ORDINAL_POSITION ,KCU2.CONSTRAINT_NAME AS REFERENCED_CONSTRAINT_NAME ,KCU2.TABLE_SCHEMA AS REFERENCED_SCHEMA_NAME ,KCU2.TABLE_NAME AS REFERENCED_TABLE_NAME ,KCU2.COLUMN_NAME AS REFERENCED_COLUMN_NAME ,KCU2.ORDINAL_POSITION AS REFERENCED_ORDINAL_POSITION FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION ) SELECT FK_SCHEMA_NAME ,FK_TABLE_NAME ,FK_CONSTRAINT_NAME --,FK_COLUMN_NAME --,REFERENCED_COLUMN_NAME , 'ALTER TABLE ' + QUOTENAME(FK_SCHEMA_NAME) + '.' + QUOTENAME(FK_TABLE_NAME) + ' ' + 'DROP CONSTRAINT ' + QUOTENAME(FK_CONSTRAINT_NAME) + '; ' AS DropStmt , 'ALTER TABLE ' + QUOTENAME(FK_SCHEMA_NAME) + '.' + QUOTENAME(FK_TABLE_NAME) + ' ADD CONSTRAINT ' + QUOTENAME(FK_CONSTRAINT_NAME) + ' FOREIGN KEY(' + SUBSTRING ( ( SELECT ', ' + QUOTENAME(FK.FK_COLUMN_NAME) AS [text()] FROM CTE AS FK WHERE FK.FK_CONSTRAINT_NAME = CTE.FK_CONSTRAINT_NAME AND FK.FK_SCHEMA_NAME = CTE.FK_SCHEMA_NAME AND FK.FK_TABLE_NAME = CTE.FK_TABLE_NAME FOR XML PATH, TYPE ).value('.[1]', 'nvarchar(MAX)') ,3, 4000 ) + ') ' + ' REFERENCES ' + QUOTENAME(REFERENCED_SCHEMA_NAME) + '.' + QUOTENAME(REFERENCED_TABLE_NAME) + '(' + SUBSTRING ( ( SELECT ', ' + QUOTENAME(Referenced.REFERENCED_COLUMN_NAME) AS [text()] FROM CTE AS Referenced WHERE Referenced.FK_CONSTRAINT_NAME = CTE.FK_CONSTRAINT_NAME AND Referenced.REFERENCED_SCHEMA_NAME = CTE.REFERENCED_SCHEMA_NAME AND Referenced.REFERENCED_TABLE_NAME = CTE.REFERENCED_TABLE_NAME FOR XML PATH, TYPE ).value('.[1]', 'nvarchar(MAX)') , 3, 4000 ) + ') ON DELETE CASCADE ; ' AS CreateStmt FROM CTE GROUP BY FK_SCHEMA_NAME ,FK_TABLE_NAME ,FK_CONSTRAINT_NAME ,REFERENCED_SCHEMA_NAME ,REFERENCED_TABLE_NAME 

而PostGreSQL的简单版本:

 ;WITH CTE AS ( SELECT KCU1.CONSTRAINT_NAME AS FK_CONSTRAINT_NAME ,KCU1.TABLE_SCHEMA AS FK_SCHEMA_NAME ,KCU1.TABLE_NAME AS FK_TABLE_NAME ,KCU1.COLUMN_NAME AS FK_COLUMN_NAME ,KCU1.ORDINAL_POSITION AS FK_ORDINAL_POSITION ,KCU2.CONSTRAINT_NAME AS REFERENCED_CONSTRAINT_NAME ,KCU2.TABLE_SCHEMA AS REFERENCED_SCHEMA_NAME ,KCU2.TABLE_NAME AS REFERENCED_TABLE_NAME ,KCU2.COLUMN_NAME AS REFERENCED_COLUMN_NAME ,KCU2.ORDINAL_POSITION AS REFERENCED_ORDINAL_POSITION FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 ON KCU2.CONSTRAINT_CATALOG = RC.UNIQUE_CONSTRAINT_CATALOG AND KCU2.CONSTRAINT_SCHEMA = RC.UNIQUE_CONSTRAINT_SCHEMA AND KCU2.CONSTRAINT_NAME = RC.UNIQUE_CONSTRAINT_NAME AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION ) SELECT FK_SCHEMA_NAME ,FK_TABLE_NAME ,FK_CONSTRAINT_NAME --,FK_COLUMN_NAME --,REFERENCED_COLUMN_NAME , 'ALTER TABLE ' || QUOTE_IDENT(FK_SCHEMA_NAME) || '.' || QUOTE_IDENT(FK_TABLE_NAME) || ' ' || 'DROP CONSTRAINT ' || QUOTE_IDENT(FK_CONSTRAINT_NAME) || '; ' AS DropStmt , 'ALTER TABLE ' || QUOTE_IDENT(FK_SCHEMA_NAME) || '.' || QUOTE_IDENT(FK_TABLE_NAME) || ' ADD CONSTRAINT ' || QUOTE_IDENT(FK_CONSTRAINT_NAME) || ' FOREIGN KEY(' || string_agg(FK_COLUMN_NAME, ', ') || ') ' || ' REFERENCES ' || QUOTE_IDENT(REFERENCED_SCHEMA_NAME) || '.' || QUOTE_IDENT(REFERENCED_TABLE_NAME) || '(' || string_agg(REFERENCED_COLUMN_NAME, ', ') || ') ON DELETE CASCADE ; ' AS CreateStmt FROM CTE GROUP BY FK_SCHEMA_NAME ,FK_TABLE_NAME ,FK_CONSTRAINT_NAME ,REFERENCED_SCHEMA_NAME ,REFERENCED_TABLE_NAME 

您将不得不改变表格,放弃FK约束并重新创build它们:

这是db2语法。 SQLServer应该是相似的

 ALTER TABLE emp DROP CONSTRAINT fk_dept; ALTER TABLE emp ADD CONSTRAINT fk_dept FOREIGN KEY(dept_no) REFERENCES dept(deptno) ON DELETE CASCADE; 

您可以编写自己的sp来查询系统表中的所有外键,然后删除它们并重新创build它们。 你必须在你的sp中使用dynamicsql来做到这一点,你可以通过fk defn循环,把它们放在一个varchar中,并追加/编辑以包含CASCADE,然后执行stmt。

@Andomar的答案为我工作,但它有点手动 – 你必须运行它,然后复制结果并运行它们。 我需要使用这个作为我的自动化testing设置的一部分,所以需要它自动运行在一个查询中。

我想出了以下的方法,让所有的SQL运行,以修改外键约束,然后实际上一次运行它:

  IF Object_id('tempdb..#queriesForContraints') IS NOT NULL BEGIN DROP TABLE #queriesForContraints END DECLARE @ignoreTablesCommaSeparated VARCHAR(1000) SELECT 'ALTER TABLE [' + ForeignKeys.foreigntableschema + '].[' + ForeignKeys.foreigntablename + '] DROP CONSTRAINT [' + ForeignKeys.foreignkeyname + ']; ' + 'ALTER TABLE [' + ForeignKeys.foreigntableschema + '].[' + ForeignKeys.foreigntablename + '] WITH CHECK ADD CONSTRAINT [' + ForeignKeys.foreignkeyname + '] FOREIGN KEY([' + ForeignKeys.foreigntablecolumn + ']) REFERENCES [' + Schema_name(sys.objects.schema_id) + '].[' + sys.objects.[name] + ']([' + sys.columns.[name] + ']) ON DELETE CASCADE; ' AS query INTO #queriesForContraints FROM sys.objects INNER JOIN sys.columns ON ( sys.columns.[object_id] = sys.objects.[object_id] ) INNER JOIN (SELECT sys.foreign_keys.[name] AS ForeignKeyName, Schema_name(sys.objects.schema_id) AS ForeignTableSchema, sys.objects.[name] AS ForeignTableName, sys.columns.[name] AS ForeignTableColumn, sys.foreign_keys.referenced_object_id AS referenced_object_id, sys.foreign_key_columns.referenced_column_id AS referenced_column_id FROM sys.foreign_keys INNER JOIN sys.foreign_key_columns ON ( sys.foreign_key_columns.constraint_object_id = sys.foreign_keys.[object_id] ) INNER JOIN sys.objects ON ( sys.objects.[object_id] = sys.foreign_keys.parent_object_id ) INNER JOIN sys.columns ON ( sys.columns.[object_id] = sys.objects.[object_id] ) AND ( sys.columns.column_id = sys.foreign_key_columns.parent_column_id )) ForeignKeys ON ( ForeignKeys.referenced_object_id = sys.objects.[object_id] ) AND ( ForeignKeys.referenced_column_id = sys.columns.column_id ) WHERE ( sys.objects.[type] = 'U' ) AND ( sys.objects.[name] NOT IN ( 'sysdiagrams' --add more comma separated table names here if required ) ) DECLARE @queryToRun NVARCHAR(MAX) SELECT @queryToRun = STUFF( (SELECT query + '' FROM #queriesForContraints FOR XML PATH ('')) , 1, 0, '') EXEC sp_executesql @statement = @queryToRun IF Object_id('tempdb..#queriesForContraints') IS NOT NULL BEGIN DROP TABLE #queriesForContraints END 

创build一个新的存储过程,其中唯一的参数是要处理的表的名称。 在这个过程中,你需要遍历sys.foreign_keys和sys.foreign_key_columns来构build适当的drop和create syntax,只需要使用光标和一些打印(KISS)。

使用以下语法调用此过程:

 EXEC sp_msforeachtable 'YourProcedureName ''?''' 

它会运行每个表。 拿走并运行输出,就完成了。