以前可能有人问过这个问题,但我找不到答案。
我在 sql-server-20XX (更可能是旧的)中有一个复杂的数据模型,其中级联删除已被关闭(按设计)。
我们的数据库的很大一部分已经被出售(我可以在顶层进行区分),并且我们有理由想要删除这些数据。我想在一个脚本中执行此操作,其中我首先打开级联删除,然后删除所有数据,然后再次关闭级联删除,但我找不到正确的语法。
需要提及的相关事项:
这些表必须保留,我需要删除组织表中的 10/33 行,并且需要删除与这些组织耦合的订单、用户...等中的所有行
SQL Server 不允许您ALTER CONSTRAINT
更改现有约束以级联删除。然而,SQL Server 很乐意让您创建相同的外键约束。因此,您不必打乱现有的 FK 约束,只需创建一些新的约束,这将帮助您进行清理,然后在完成后可以删除这些特殊用途的约束。
考虑这个非常简单的模式:
CREATE DATABASE [CascadeOnDelete];
GO
USE [CascadeOnDelete];
CREATE TABLE dbo.Customers (
CustomerID int,
CustomerName nvarchar(100),
CONSTRAINT PK_Customers PRIMARY KEY CLUSTERED (CustomerID)
);
INSERT dbo.Customers (CustomerID, CustomerName)
VALUES (1,'Grandpa Joe'),(2,'Grandma Josephine'),
(3,'Grandpa George'),(4,'Grandma Gerorgina'),
(5,'Charlie Bucket');
CREATE TABLE dbo.Orders(
OrderID int,
CustomerID int
CONSTRAINT FK_Orders_CustomerID
REFERENCES Customers(CustomerID),
OrderDetails nvarchar(100),
CONSTRAINT PK_Parents PRIMARY KEY CLUSTERED (OrderID)
);
INSERT dbo.Orders (OrderID, CustomerID, OrderDetails)
VALUES (1,1,'Wonka bar'),
(2,5,'Wonka bar'),
(3,5,'Wonka Scrumdiddlyumptious Bar');
SELECT * FROM Customers;
SELECT * FROM Orders;
Run Code Online (Sandbox Code Playgroud)
如果您尝试删除 Grampa Joe,您会收到错误消息,因为 Grampa Joe 有一个订单:
DELETE c
FROM dbo.Customers AS c
WHERE CustomerID = 1;
/*
Msg 547, Level 16, State 0, Line 33
The DELETE statement conflicted with the REFERENCE constraint "FK_Orders_CustomerID". The conflict occurred in database "CascadeOnDelete", table "dbo.Orders", column 'CustomerID'.
*/
Run Code Online (Sandbox Code Playgroud)
现在我们已经完成了设置,我们可以编写我们需要的其余内容的脚本:
首先,我们可以使用一些动态 SQL 为数据库中的每个 FK 生成脚本。我正在附加名称以_CASCADEDELETES
识别它们。我也在创建这些WITH NOCHECK
,ON DELETE CASCADE
无论原始的 FK 定义如何。您将获取此脚本的输出,检查它,然后运行它以复制数据库中的每个 FK,并添加以下内容:
--adapted from https://www.mssqltips.com/sqlservertip/3347/drop-and-recreate-all-foreign-key-constraints-in-sql-server/
SELECT N'ALTER TABLE '
+ QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name) + N' WITH NOCHECK'
+ N' ADD CONSTRAINT ' + QUOTENAME(fk.name + N'_CASCADEDELETES')
+ N' FOREIGN KEY ('
+ STUFF((SELECT ',' + QUOTENAME(c.name)
FROM sys.columns AS c
JOIN sys.foreign_key_columns AS fkc
ON fkc.parent_column_id = c.column_id
AND fkc.parent_object_id = c.object_id
WHERE fkc.constraint_object_id = fk.object_id
ORDER BY fkc.constraint_column_id
FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'')
+ N') REFERENCES ' + QUOTENAME(rs.name) + '.' + QUOTENAME(rt.name)
+ N'('
+ STUFF((SELECT ',' + QUOTENAME(c.name)
FROM sys.columns AS c
JOIN sys.foreign_key_columns AS fkc
ON fkc.referenced_column_id = c.column_id
AND fkc.referenced_object_id = c.[object_id]
WHERE fkc.constraint_object_id = fk.[object_id]
ORDER BY fkc.constraint_column_id
FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'')
+ N')'
+ N' ON DELETE CASCADE;'
FROM sys.foreign_keys AS fk
INNER JOIN sys.tables AS rt -- referenced table
ON fk.referenced_object_id = rt.[object_id]
INNER JOIN sys.schemas AS rs
ON rt.[schema_id] = rs.[schema_id]
INNER JOIN sys.tables AS ct -- constraint table
ON fk.parent_object_id = ct.[object_id]
INNER JOIN sys.schemas AS cs
ON ct.[schema_id] = cs.[schema_id]
WHERE rt.is_ms_shipped = 0 AND ct.is_ms_shipped = 0;
Run Code Online (Sandbox Code Playgroud)
在我们的示例数据库中,我们只有一个 FK,因此输出会为该一个 FK 生成一个新的 FK:
ALTER TABLE [dbo].[Orders] WITH NOCHECK
ADD CONSTRAINT [FK_Orders_CustomerID_CASCADEDELETES] FOREIGN KEY ([CustomerID])
REFERENCES [dbo].[Customers]([CustomerID])
ON DELETE CASCADE;
Run Code Online (Sandbox Code Playgroud)
创建该 FK 后,我们可以简单地再次运行原始删除,从表中删除 Grampa Joe Customers
,它也会级联并删除Orders
:
DELETE c
FROM dbo.Customers AS c
WHERE CustomerID = 1;
SELECT *
FROM dbo.Orders
Run Code Online (Sandbox Code Playgroud)
我们就完成了!最后一步是简单地删除我们创建的重复 FK。我们可以使用另一个脚本来生成DROP
命令,就像我们在创建时所做的那样。我们可以简单地查看并运行此脚本的输出,以根据_CASCADE DELETES
我们添加到名称的附加内容删除 FK:
--adapted from https://www.mssqltips.com/sqlservertip/3347/drop-and-recreate-all-foreign-key-constraints-in-sql-server/
SELECT N'
ALTER TABLE ' + QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name)
+ ' DROP CONSTRAINT ' + QUOTENAME(fk.name) + ';'
FROM sys.foreign_keys AS fk
INNER JOIN sys.tables AS ct
ON fk.parent_object_id = ct.[object_id]
INNER JOIN sys.schemas AS cs
ON ct.[schema_id] = cs.[schema_id]
WHERE fk.name LIKE '%CASCADEDELETES';
Run Code Online (Sandbox Code Playgroud)
这样做的好处是,您不必担心如何将原始 FK 恢复到开始时的状态,因为您永远不必碰它们。如果您的所有 FK 都完全受信任,并且您希望避免重新创建所有 FKWITH CHECK
以确保它们最终受信任的潜在阻塞或运行时问题,这可能特别有用。
归档时间: |
|
查看次数: |
1456 次 |
最近记录: |