In SQL Server 2005, can I do a cascade delete without setting the property on my tables?

前端 未结 13 1459
心在旅途
心在旅途 2020-12-12 17:02

I have a database full of customer data. It\'s so big that it\'s really cumbersome to operate on, and I\'d rather just slim it down to 10% of the customers, which is plenty

相关标签:
13条回答
  • 2020-12-12 17:39

    Post here a script that will work with foreign keys contain more than one column.

    create procedure usp_delete_cascade (
    @TableName varchar(200), @Where nvarchar(1000)
    ) as begin
    
    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 default(0),
        delete_sql varchar(1000)
    )
                DECLARE @MyCursor CURSOR
    
    declare         @referencing_column_name varchar(1000)
    declare         @referencing_table_name varchar(1000)
     declare @Sql nvarchar(4000)
    insert into @to_delete (criteria, table_name) values ('', @TableName)
    
    
    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
            SET @MyCursor = CURSOR FAST_FORWARD
            FOR
            select referencing_column.name as column_name,
                referencing_table.name as table_name
            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
    
            OPEN @MyCursor
            FETCH NEXT FROM @MYCursor
            INTO @referencing_column_name, @referencing_table_name
    
            WHILE @@FETCH_STATUS = 0
    
            BEGIN
                PRINT @referencing_column_name
                PRINT @referencing_table_name
                        update @to_delete set criteria = criteria + ' AND '+@table_name+'.'+@referencing_column_name+'='+ @referencing_table_name+'.'+@referencing_column_name
                        where table_name = @referencing_table_name
    
                        if(@@ROWCOUNT = 0)
                        BEGIN
                                --if(@id <> 1)
                                --BEGIN
                                    insert into @to_delete (criteria, table_name)
                                    VALUES( ' LEFT JOIN '+@table_name+' ON '+@table_name+'.'+@referencing_column_name+'='+ @referencing_table_name+'.'+@referencing_column_name+ @criteria,
                                    @referencing_table_name
                                    )
                                --END
                                --ELSE
                                --BEGIN
                                    --insert into @to_delete (criteria, table_name)
                                    --VALUES( ' LEFT JOIN '+@table_name+' ON '+@table_name+'.'+@referencing_column_name+'='+ @referencing_table_name+'.'+@referencing_column_name,
                                    --@referencing_table_name
                                    --)
                                --END
                        END
                            FETCH NEXT FROM @MYCursor
                INTO @referencing_column_name, @referencing_table_name
            END
    
    
            CLOSE @MyCursor 
            DEALLOCATE @MyCursor 
        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
    
    --select id, table_name, criteria, @Where from @to_delete order by id desc
    
    select @id = max(id) from @to_delete
    while (@id >= 1)
    begin
        select @criteria = criteria, @table_name = table_name from @to_delete where id = @id
        set @Sql = 'delete [' + @table_name + '] from [' + @table_name + '] ' + @criteria+' WHERE '+@Where
        exec (@Sql)
        PRINT @Sql
    
        -- Next record
        set @id = @id - 1
    end
    end
    
    0 讨论(0)
  • 2020-12-12 17:40

    Combining your advice and a script I found online, I made a procedure that will produce SQL you can run to perform a cascaded delete regardless of ON DELETE CASCADE. It was probably a big waste of time, but I had a good time writing it. An advantage of doing it this way is, you can put a GO statement between each line, and it doesn't have to be one big transaction. The original was a recursive procedure; this one unrolls the recursion into a stack table.

    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'
    
    0 讨论(0)
  • 2020-12-12 17:42

    I usually just hand write the queries to delete the records I don't want and save that as a .sql file for future reference. The pseudocode is:

    1. select id's of records from the main table that I want to delete into a temp table
    2. write a delete query for each related table which joins to the temp table.
    3. write a delete query for the main table joining to my temp table.
    0 讨论(0)
  • 2020-12-12 17:45

    My suggestion is to go ahead and write a script that will add the on delete cascade to each relationship in the database while exporting a list of modified relationships. Then you can reverse the process and remove the on delete cascade command on each table in the list.

    0 讨论(0)
  • 2020-12-12 17:47

    Expansion of croisharp's answer to take triggers into consideration, i.e. schema-aware solution that disables all affecting triggers, deletes rows, and enables the triggers.

    CREATE PROCEDURE usp_delete_cascade (
    @base_table_schema varchar(100),
    @base_table_name varchar(200),
    @base_criteria nvarchar(1000)
    )
    as begin
    
        -- 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_schema varchar(100),
                table_name varchar(200) not null,
                processed bit not null,
                delete_sql varchar(1000)
        )
    
        insert into @to_delete (criteria, table_schema, table_name, processed) values (@base_criteria, @base_table_schema, @base_table_name, 0)
    
        declare @id int, @criteria nvarchar(1000), @table_name varchar(200), @table_schema varchar(100)
        while exists(select 1 from @to_delete where processed = 0) begin
                select top 1 @id = id, @criteria = criteria, @table_name = table_name, @table_schema = table_schema from @to_delete where processed = 0 order by id desc
    
                insert into @to_delete (criteria, table_schema, table_name, processed)
                        select referencing_column.name + ' in (select [' + referenced_column.name + '] from [' + @table_schema + '].[' + @table_name +'] where ' + @criteria + ')',
                                schematable.name,
                                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.schemas schematable on referencing_table.schema_id = schematable.schema_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_schema + '].[' + table_name + '] where ' + criteria from @to_delete order by id desc
    
        DECLARE @commandText VARCHAR(8000), @triggerOn VARCHAR(8000), @triggerOff VARCHAR(8000)
        DECLARE curDeletes CURSOR FOR
            select
                'DELETE FROM [' + table_schema + '].[' + table_name + '] WHERE ' + criteria,
                'ALTER TABLE [' + table_schema + '].[' + table_name + '] DISABLE TRIGGER ALL',
                'ALTER TABLE [' + table_schema + '].[' + table_name + '] ENABLE TRIGGER ALL'
            from @to_delete order by id desc
    
        OPEN curDeletes
        FETCH NEXT FROM curDeletes INTO @commandText, @triggerOff, @triggerOn
    
        WHILE(@@FETCH_STATUS=0)
        BEGIN
            EXEC (@triggerOff)
            EXEC (@commandText)
            EXEC (@triggerOn)
            FETCH NEXT FROM curDeletes INTO @commandText, @triggerOff, @triggerOn
        END
        CLOSE curDeletes
        DEALLOCATE curDeletes
    end
    
    0 讨论(0)
  • 2020-12-12 17:50

    Kevin post is incomplete, his t-sql sp only prints the command, to execute these command, before last end add this

    DECLARE @commandText VARCHAR(8000)
            DECLARE curDeletes CURSOR FOR
                select 'delete from [' + table_name + '] where ' + criteria from @to_delete order by id desc
    
            OPEN curDeletes
            FETCH NEXT FROM curDeletes
            INTO
                @commandText
    
            WHILE(@@FETCH_STATUS=0)
            BEGIN
                EXEC (@commandText)
                FETCH NEXT FROM curDeletes INTO @commandText
            END
            CLOSE curDeletes
            DEALLOCATE curDeletes
    
    0 讨论(0)
提交回复
热议问题