我有一个包含大约250个表的SQL 2005数据库。
我想暂时为所有外键启用ON DELETE CASCADE,以便我可以轻松地进行批量删除。
然后我要关闭所有外键上的ON DELETE CASCADE。
我知道这样做的唯一方法是使用Management Studio生成完整的数据库创建脚本,进行某种搜索和替换以除去外键以外的所有内容,保存脚本,然后再做一些搜索和替换以添加ON DELETE CASCADE。
然后我运行脚本,执行删除操作,然后运行其他脚本。
是否有更简单的方法来制作此脚本?这种方法似乎太容易出错,我将不得不更新我们对数据库所做的任何其他更改,或者每次我需要使用它时手动重新生成它。
是否可以在系统表上运行select来为我“生成”脚本?甚至可以在启用和禁用ON DELETE CASCADE?
的系统表上运行更新答案 0 :(得分:69)
这是我用于类似目的的脚本。它不支持复合外键(使用多个字段)。它可能需要一些调整才能适合您的情况。 编辑:特别是它无法正确处理多列外键。
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'))
答案 1 :(得分:11)
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
。
这仍然不能处理在两个表中将列命名为不同内容的情况 - 需要为其定义另一个用户定义的函数。
答案 2 :(得分:7)
更符合标准的解决方案:
;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
ORDER BY FK.FK_ORDINAL_POSITION
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
ORDER BY Referenced.REFERENCED_ORDINAL_POSITION
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
答案 3 :(得分:3)
您必须更改表格,删除FK约束并重新创建它们:
这是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来查询系统表中的所有外键,删除它们并重新创建它们。您必须在sp中使用动态sql来执行此操作,您可以循环遍历fk defn,将它们放入varchar并附加/编辑以包含CASCADE,然后执行stmt。
答案 4 :(得分:1)
@Andomar的回答对我有用,但它有点手动 - 你必须运行它,然后复制结果并运行它们。我需要将其用作自动化测试设置的一部分,因此需要它自动在一个查询中运行。
我已经提出了以下内容,它将运行所有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
答案 5 :(得分:0)
创建一个新的存储过程,其中唯一的参数是要处理的表的名称。在该过程中,您需要遍历sys.foreign_keys和sys.foreign_key_columns以构建正确的drop并创建语法,只需使用游标和一些打印(KISS)。
使用以下语法调用此过程:
EXEC sp_msforeachtable 'YourProcedureName ''?'''
它将为每个表运行。拿起并运行输出,你就完成了。
答案 6 :(得分:0)
我发现一个简单的解决方案是将数据库导出到单个文件,使用搜索功能将所有NO ACTION替换为CASCADE,删除数据库并导入编辑的文件。
数据库在导出和导入之间更改的信息将丢失。