在SQL Server 2005中,是否可以在不设置表属性的情况下进行级联删除?

时间:2020-03-06 14:33:34  来源:igfitidea点击:

我有一个充满客户数据的数据库。它是如此之大,以至于操作起来确实很麻烦,我宁愿将它缩小到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_MyFKALTER 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'