0

我想要达到的目标如下。当我删除一条记录时,我想检查是否有任何 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

显然你不能嵌套一个插入执行程序。但是我真的没有看到任何其他方式来做到这一点。我尝试将其转换为函数,但还有其他问题,例如动态查询。

任何帮助将不胜感激。

4

1 回答 1

1

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

在外部过程中创建一个#results 临时表,然后调用内部过程。

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

这样您就可以节省大量时间,因为您不必移动数据。你也不必再筑巢INSERT...EXEC了。

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

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

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

于 2013-05-28T15:17:43.243 回答