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

明らかに、insert exec をネストすることはできません。しかし、私はそれを行う他の方法を本当に見ていません。関数に変換しようとしましたが、動的クエリのような他の問題があります。

どんな助けでも大歓迎です。

4

1 に答える 1

1

プロシージャを外部プロシージャと内部プロシージャに分割します。

外側のプロシージャで #results temp-table を作成してから、内側のプロシージャを呼び出します。

内部プロシージャでは、再帰を含むすべてのロジックを配置しますが、最後に結果を選択する代わりに、既存の #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 に答える