在SQL Server 2005中,是否可以在不设置表属性的情况下进行级联删除?
我有一个充满客户数据的数据库。它是如此之大,以至于操作起来确实很麻烦,我宁愿将它缩小到10%的客户,这对于开发来说是很大的。我的桌子太多了,我不想用" ON DELETE CASCADE"更改它们,特别是因为这是一次性交易。
我可以执行删除操作而无需先设置所有表的级联操作吗?如果没有,我最好的选择是什么?
解决方案
我通常只需要手写查询来删除不需要的记录,然后将其另存为.sql文件以备将来参考。伪代码为:
- 从主表中选择要删除到临时表中的记录的ID
- 为连接到临时表的每个相关表编写一个删除查询。
- 为连接到我的临时表的主表编写一个删除查询。
我的建议是继续编写一个脚本,该脚本将在数据库中的每个关系上添加on delete级联,同时导出已修改关系的列表。然后,我们可以撤消该过程,并删除列表中每个表上的on delete级联命令。
就个人而言,如果我们要将记录保留在生产中,我也将其保留在开发中。否则,当记录集较小时,我们可能会编写运行良好的代码,但面对实际记录集时,则可能会超时。
但是,如果我们确定要这样做,则可以将我们要从主表确定的记录的id字段首先复制到工作表中。然后,我将使用每个相关的表,并编写一个加入该工作表的删除操作,以仅删除那些记录。完成父表。确保将此内容写在脚本中并保存下来,以便下次我们想对测试数据进行类似操作时,可以轻松地运行它,而不必弄清楚需要删除记录的相关表是什么。
进入SQL Server Management Studio,然后右键单击数据库。选择任务->生成脚本。单击下一步两次。在"选项"窗口上,选择将其设置为仅生成CREATE语句,然后将除外键之外的所有内容都设置为False。点击下一步。选择表,然后再次单击下一步。单击"全选"按钮,然后单击"下一步",然后单击"完成",然后将脚本发送到我们选择的查询窗口或者文件中(不要使用剪贴板,因为它可能是一个大脚本)。现在,删除所有添加表的脚本,然后应该留下一个脚本来创建外键。
复制该脚本,因为这是将数据库恢复到当前状态的方式。使用搜索和替换将ON DELETE CASCADE添加到每个约束的末尾。这可能会因当前FK的设置方式而异,并且可能需要进行一些手动编辑。
重复脚本生成,但是这次将其设置为仅生成DROP语句。确保手动删除生成的表放置。运行放置,然后运行编辑后的创建,使其在删除时全部级联。进行删除,再次运行放置脚本,然后运行开始时保存的脚本。
还要先备份数据库!即使只是一个开发数据库,如果部分脚本不太正确,它也会为我们省去一些麻烦。
希望这可以帮助!
顺便说一句,我们绝对应该按照完整的测试数据进行测试,如另一个建议的那样,但是我可以理解为什么我们可能不需要进行初始开发。只是不要忘记在某些时候将其作为质量检查的一部分。
除非我们希望维护Chris提出的所有相关查询,否则ON DELETE CASCADE是迄今为止最快,最直接的解决方案。而且,如果我们不希望它是永久性的,为什么不使用一些T-SQL代码来像下面这样打开和关闭此选项。
- 删除原始的Tbl_A_MyFK约束(没有ON DELETE级联)ALTER TABLE Tbl_A DROP CONSTRAINT Tbl_A_MyFK
- 使用ON DELETE CASCADE设置约束
Tbl_A_MyFK
ALTER TABLE Tbl_A ADD CONSTRAINT Tbl_A_MyFK FOREIGN KEY(MyFK)参考Tbl_B(Column)ON DELETE CASCADE` - 在这里,我们可以删除`从Tbl_A WHERE ...删除...
- 删除约束
Tbl_A_MyFK``ALTER TABLE Tbl_A DROP CONSTRAINT Tbl_A_MyFK
- 设置约束" Tbl_A_MyFK",而不设置" ON DELETE级联" ALTER TABLE Tbl_A添加约束Tbl_A_MyFK外键(MyFK)参考(Tbl_B)
结合建议和我在网上找到的脚本,我制作了一个过程,该过程将生成可运行以执行级联删除的SQL,而不管" ON DELETE CASCADE"如何。这可能是浪费时间,但是我写得很好。这样做的好处是,我们可以在每行之间放置一个" GO"语句,而不必是一个大事务。原来是递归程序;这将递归展开到堆栈表中。
create procedure usp_delete_cascade ( @base_table_name varchar(200), @base_criteria nvarchar(1000) ) as begin -- Adapted from http://www.sqlteam.com/article/performing-a-cascade-delete-in-sql-server-7 -- Expects the name of a table, and a conditional for selecting rows -- within that table that you want deleted. -- Produces SQL that, when run, deletes all table rows referencing the ones -- you initially selected, cascading into any number of tables, -- without the need for "ON DELETE CASCADE". -- Does not appear to work with self-referencing tables, but it will -- delete everything beneath them. -- To make it easy on the server, put a "GO" statement between each line. declare @to_delete table ( id int identity(1, 1) primary key not null, criteria nvarchar(1000) not null, table_name varchar(200) not null, processed bit not null, delete_sql varchar(1000) ) insert into @to_delete (criteria, table_name, processed) values (@base_criteria, @base_table_name, 0) declare @id int, @criteria nvarchar(1000), @table_name varchar(200) while exists(select 1 from @to_delete where processed = 0) begin select top 1 @id = id, @criteria = criteria, @table_name = table_name from @to_delete where processed = 0 order by id desc insert into @to_delete (criteria, table_name, processed) select referencing_column.name + ' in (select [' + referenced_column.name + '] from [' + @table_name +'] where ' + @criteria + ')', referencing_table.name, 0 from sys.foreign_key_columns fk inner join sys.columns referencing_column on fk.parent_object_id = referencing_column.object_id and fk.parent_column_id = referencing_column.column_id inner join sys.columns referenced_column on fk.referenced_object_id = referenced_column.object_id and fk.referenced_column_id = referenced_column.column_id inner join sys.objects referencing_table on fk.parent_object_id = referencing_table.object_id inner join sys.objects referenced_table on fk.referenced_object_id = referenced_table.object_id inner join sys.objects constraint_object on fk.constraint_object_id = constraint_object.object_id where referenced_table.name = @table_name and referencing_table.name != referenced_table.name update @to_delete set processed = 1 where id = @id end select 'print ''deleting from ' + table_name + '...''; delete from [' + table_name + '] where ' + criteria from @to_delete order by id desc end exec usp_delete_cascade 'root_table_name', 'id = 123'