检索所有外键及其记录

时间:2013-05-28 13:56:44

标签: sql sql-server-2008 recursion nested procedure

我想要实现的目标如下。 当我删除一条记录时,我想检查是否存在任何FK关系,并且需要递归。这样我就可以显示与你要删除的记录相关的所有记录的列表。

这是嵌套链接的一个小例子 项目1 - >阶段1 - >块1 - > ..

因此,当我尝试删除项目1时,我需要先获取您需要删除的项目列表: 阶段1 块1 ....

我想用一个带有ID和表名的存储过程(格式[chema]。[tablename])来查找所有这些链接记录。

我遇到的问题是递归部分。 到目前为止,这是我的代码:

ALTER PROCEDURE core.usp_CanBeDeleted    
    @entityId int,
    @entityName nvarchar(250)   
AS
BEGIN   
    DECLARE @NumberRecords int, @RowCount int
    DECLARE @childId int
    DECLARE @query nvarchar(max)
    DECLARE @eName nvarchar(250) , @keyName nvarchar(250) 
    DECLARE @columnName nvarchar(250)

    DECLARE @keys TABLE(
        RowID int IDENTITY(1, 1), 
        name nvarchar(250),
        entityName nvarchar(250),
        columnName nvarchar(250)
    )

    if not exists (select * from sysobjects where name='partialResults' and xtype='U')
    BEGIN
        CREATE TABLE partialResults(
            RowID int IDENTITY(1, 1), 
            id int,
            parentId int,
            name nvarchar(250),
            FK_name nvarchar(250)
        )
    END

    DECLARE @recusiveResults TABLE(
        RowID int, 
        id int,
        parentId int,
        name nvarchar(250),
        FK_name nvarchar(250)
    )

    DECLARE @results TABLE(
        RowID int, 
        id int,
        parentId int,
        name nvarchar(250),
        FK_name nvarchar(250)
    )

    SET @RowCount = 1

        -- get all FK's of the entity
    INSERT INTO @keys
    SELECT name, '[' +  OBJECT_SCHEMA_NAME(parent_object_id) + '].[' +     OBJECT_NAME(parent_object_id)+ ']',cu.column_name
    from sys.foreign_keys k
    INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
    ON k.name = CU.CONSTRAINT_NAME
    where k.referenced_object_id = OBJECT_ID(@entityName)

    -- set variable to number of records in temp table
    SET @NumberRecords = @@ROWCOUNT

    -- loop through the FK's an get all linked entities
    WHILE(@RowCount <= @NumberRecords)
    BEGIN

        SELECT @keyName = name, @eName = entityName, @columnName = columnName
        FROM @keys
        WHERE RowId = @RowCount 

        -- get all FK information
        SET @query = 'INSERT INTO partialResults(FK_name, name, id, parentId)'
        + ' SELECT ''' + @keyName + ''','''+  @eName + ''',' + 'id,' +         cast(@entityId as varchar(25)) + ' as parentid'
        + ' FROM  ' +@eName
        + ' WHERE id in '
        + ' (SELECT ' + @columnName 
            + ' FROM ' + @entityName 
            + ' WHERE id = ' + cast(@entityId as varchar(25))
            + ' )'

        --print @query                                              
        EXEC (@query)

        SET @RowCount = @RowCount + 1
    END

    -- rest number of records
    SET @RowCount = 1
    SELECT @NumberRecords = count(id) 
    FROM partialResults 

    -- save partialResults  
    INSERT INTO @results--(FK_name, name, id, parentId)
    SELECT *--FK_name, name, id, parentId
    FROM partialResults 

    DELETE FROM partialResults

    WHILE(@RowCount <= @NumberRecords)
        BEGIN
        -- select next row
        SELECT @childId = id, @eName = name
        FROM @results
        WHERE RowId = @RowCount                                 

        INSERT INTO @recusiveResults        
        EXEC core.usp_CanBeDeleted @childId, @eName

        SET @RowCount = @RowCount + 1
    END

    INSERT INTO @results
    SELECT *
    FROM @recusiveResults

    if exists (select * from sysobjects where name='partialResults' and xtype='U')
    BEGIN
        -- remove temp tables
        DROP TABLE partialResults
    END
    -- return results
    SELECT * 
    FROM @results       
END
GO
问题出在这里:

INSERT INTO @recusiveResults        
EXEC core.usp_CanBeDeleted @childId, @eName

显然你不能嵌套插入exec。 但我真的没有看到任何其他方法来做到这一点。 我已经尝试将其转换为函数,但还有其他问题,如动态查询。

任何帮助都会有很大帮助。

1 个答案:

答案 0 :(得分:1)

将过程拆分为外部和内部过程。

在外部过程中创建一个#results temp-table,然后调用内部过程。

在内部过程中放入包括递归在内的所有逻辑,但不是在结尾处选择结果而是将结果插入到已存在的#results表中。

这样你就可以节省很多时间,因为你不必移动数据。您也不必再嵌套INSERT...EXEC了。

您也不再需要dbo.PartialResults表,因为您可以直接写入动态语句中的#results表。如果您仍然需要它,为了使递归工作用您在内部过程中创建的#partialResults临时表替换它(不要检查是否存在,只需创建新的。请参阅http://sqlity.net/en/1109/temp-tables-scoping-eclipsing/以获取解释临时表范围)。这样每次执行都会创建自己的临时表,而不必处理清理。与使用真实表格相比,这也稍微不那么重了。

最后,所有表变量也可以。

在内部程序结束时,您可以执行简单的SELECT * FROM #results;输出所有收集的结果。