用于删除给定表

时间:2017-10-13 18:29:48

标签: sql sql-server sql-server-2008-r2

我需要创建脚本以查找与给定表相关的所有与外键相关的表,并使用sql server动态删除结果输出表的记录以及带有层次结构的给定表

这是存储过程,我试图用于查找与层次结构的所有F.K关系,但我认为它不返回所有外键关系记录:

create proc dbo.usp_SearchFK 
  @table varchar(256) -- use two part name convention
, @lvl int=0 -- do not change
, @ParentTable varchar(256)='' -- do not change
, @debug bit = 1
as
begin
    set nocount on;
    declare @dbg bit;
    set @dbg=@debug;
    if object_id('tempdb..#tbl', 'U') is null
        create table  #tbl  (id int identity, tablename varchar(256), lvl int, ParentTable varchar(256));
    declare @curS cursor;
    if @lvl = 0
        insert into #tbl (tablename, lvl, ParentTable)
        select @table, @lvl, Null;
    else
        insert into #tbl (tablename, lvl, ParentTable)
        select @table, @lvl,@ParentTable;
    if @dbg=1   
        print replicate('----', @lvl) + 'lvl ' + cast(@lvl as varchar(10)) + ' = ' + @table;

    if not exists (select * from sys.foreign_keys where referenced_object_id = object_id(@table))
        return;
    else
    begin -- else
        set @ParentTable = @table;
        set @curS = cursor for
        select tablename=object_schema_name(parent_object_id)+'.'+object_name(parent_object_id)
        from sys.foreign_keys 
        where referenced_object_id = object_id(@table)
        and parent_object_id <> referenced_object_id; -- add this to prevent self-referencing which can create a indefinitive loop;

        open @curS;
        fetch next from @curS into @table;

        while @@fetch_status = 0
        begin --while
            set @lvl = @lvl+1;
            -- recursive call
            exec dbo.usp_SearchFK @table, @lvl, @ParentTable, @dbg;
            set @lvl = @lvl-1;
            fetch next from @curS into @table;
        end --while
        close @curS;
        deallocate @curS;
    end -- else
    if @lvl = 0
        select * from #tbl;
    return;
end
go

以下脚本使用上述SP并创建删除语句:

insert into #tmp 
exec dbo.usp_SearchFK @table='dbo.M', @debug=0;

declare @where varchar(max) ='where M.id=2' -- if @where clause is null or empty, it will delete tables as a whole with the right order
declare @curFK cursor, @fk_object_id int;
declare @sqlcmd varchar(max)='', @crlf char(2)=char(0x0d)+char(0x0a);
declare @child varchar(256), @parent varchar(256), @lvl int, @id int;
declare @i int;
declare @t table (tablename varchar(128));
declare @curT cursor;
if isnull(@where, '')= ''
begin
    set @curT = cursor for select tablename, lvl from #tmp order by lvl desc
    open @curT;
    fetch next from @curT into @child, @lvl;
    while @@fetch_status = 0
    begin -- loop @curT
        if not exists (select 1 from @t where tablename=@child)
            insert into @t (tablename) values (@child);
        fetch next from @curT into @child, @lvl;
    end -- loop @curT
    close @curT;
    deallocate @curT;

    select  @sqlcmd = @sqlcmd + 'delete from ' + tablename + @crlf from @t ;
    print @sqlcmd;
end
else
begin 
    declare curT cursor for
    select  lvl, id
    from #tmp
    order by lvl desc;

    open curT;
    fetch next from curT into  @lvl, @id;
    while @@FETCH_STATUS =0
    begin
        set @i=0;
        if @lvl =0
        begin -- this is the root level
            select @sqlcmd = 'delete from ' + tablename from #tmp where id = @id;
        end -- this is the roolt level

        while @i < @lvl
        begin -- while

            select top 1 @child=TableName, @parent=ParentTable from #tmp where id <= @id-@i and lvl <= @lvl-@i order by lvl desc, id desc;
            set @curFK = cursor for
            select object_id from sys.foreign_keys 
            where parent_object_id = object_id(@child)
            and referenced_object_id = object_id(@parent)

            open @curFK;
            fetch next from @curFk into @fk_object_id
            while @@fetch_status =0
            begin -- @curFK

                if @i=0
                    set @sqlcmd = 'delete from ' + @child + @crlf +
                    'from ' + @child + @crlf + 'inner join ' + @parent  ;
                else
                    set @sqlcmd = @sqlcmd + @crlf + 'inner join ' + @parent ;

                ;with c as 
                (
                    select child = object_schema_name(fc.parent_object_id)+'.' + object_name(fc.parent_object_id), child_col=c.name
                    , parent = object_schema_name(fc.referenced_object_id)+'.' + object_name(fc.referenced_object_id), parent_col=c2.name
                    , rnk = row_number() over (order by (select null))
                    from sys.foreign_key_columns fc
                    inner join sys.columns c
                    on fc.parent_column_id = c.column_id
                    and fc.parent_object_id = c.object_id
                    inner join sys.columns c2
                    on fc.referenced_column_id = c2.column_id
                    and fc.referenced_object_id = c2.object_id
                    where fc.constraint_object_id=@fk_object_id
                )
                    select @sqlcmd =@sqlcmd +  case rnk when 1 then ' on '  else ' and ' end 
                    + @child +'.'+ child_col +'='  +  @parent   +'.' + parent_col
                    from c;
                    fetch next from @curFK into @fk_object_id;
            end --@curFK
            close @curFK;
            deallocate @curFK;
            set @i = @i +1;
        end --while
        print @sqlcmd + @crlf + @where + ';';
        print '';
        fetch next from curT into  @lvl, @id;
    end
    close curT;
    deallocate curT;
end

go

0 个答案:

没有答案