14

これが私がVBScriptサブルーチンとして持っているものです:

sub buildChildAdminStringHierarchical(byval pAdminID, byref adminString)
    set rsx = conn.execute ("select admin_id from administrator_owners where admin_id not in (" & adminString & ") and owner_id = " & pAdminID)

    do while not rsx.eof
        adminString = adminString & "," & rsx(0)
        call buildChildAdminStringHierarchical(rsx(0),adminString)
        rsx.movenext
    loop
end sub

サブルーチンに再帰呼び出しがあるので、これをストアドプロシージャに変換する方法はありますか?

これが私が試したことです...

CREATE PROCEDURE usp_build_child_admin_string_hierarchically
    @ID AS INT,
    @ADMIN_STRING AS VARCHAR(8000),
    @ID_STRING AS VARCHAR(8000) OUTPUT
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    
    DECLARE @index int;
    DECLARE @length int;
    DECLARE @admin_id int;
    DECLARE @new_string varchar(8000);
    
    SET @index = 1;
    SET @length = 0;
    SET @new_string = @ADMIN_STRING;
    
    CREATE TABLE #Temp (ID int)
    
    WHILE @index <= LEN(@new_string)
    BEGIN
        IF CHARINDEX(',', @new_string, @index) = 0
            SELECT @length = (LEN(@new_string) + 1) - @index;
        ELSE
            SELECT @length = (CHARINDEX(',', @new_string, @index) - @index);
        SELECT @admin_id = CONVERT(INT,SUBSTRING(@new_string, @index, @length));
        SET @index = @index + @length + 1;
        INSERT INTO #temp VALUES(@admin_id);
    END
    
    DECLARE TableCursor CURSOR FOR
        SELECT Admin_ID FROM Administrator_Owners WHERE Admin_ID NOT IN (SELECT ID FROM #temp) AND Owner_ID = @ID;

    OPEN TableCursor;
    FETCH NEXT FROM TableCursor INTO @admin_id;

    WHILE @@FETCH_STATUS = 0
    BEGIN
        IF LEN(@ID_STRING) > 0
        SET @ID_STRING = @ID_STRING + ',' + CONVERT(VARCHAR, @admin_id);
        ELSE
        SET @ID_STRING = CONVERT(VARCHAR, @admin_id);
        
        EXEC usp_build_child_admin_string_hierarchically @admin_id, @ID_STRING, @ID_STRING;

        FETCH NEXT FROM TableCursor INTO @admin_id;
    END

    CLOSE TableCursor;
    DEALLOCATE TableCursor;
    
    DROP TABLE #temp;
END
GO

しかし、そのストアドプロシージャが呼び出されると、次のエラーが発生します...

A cursor with the same name 'TableCursor' already exists.

4

3 に答える 3

46

LOCAL次のようにカーソルを指定できます。

DECLARE TableCursor CURSOR LOCAL FOR
SELECT ...

少なくともSQLServer2008 R2(私のマシン)では、これにより、「カーソルが既に存在します」というエラーが発生することなく、sprocを再帰的に呼び出すことができます。

于 2012-04-26T04:05:37.200 に答える
11

問題は、カーソルがグローバルではない、セッションカーソルであるということです。再帰を実行しているため、各反復で新しいprocスコープにカーソルが作成されていても、それらはすべて同じPID(接続)で同時に作成されているため、衝突が発生します。

再帰中に再現されないいくつかの基準に基づいて、プロシージャの各反復で一意のカーソル名を生成する必要があります。

または、できれば、setロジックを使用して必要なことを実行する方法を見つけ、再帰CTEを使用して必要な再帰を処理します。

于 2010-06-28T17:28:50.317 に答える
2

できますが、通常はお勧めできません。SQLはセットベースの操作用に作成されています。また、少なくともMS SQL Serverでは、再帰は実行できる再帰呼び出しの数に制限されています。ネストできるのは最大32レベルまでです。

あなたの場合の問題は、CURSORが各呼び出しを通して持続するため、複数回作成することになります。

于 2010-06-28T17:26:54.093 に答える