SQL Server中是否可以递归调用存储过程?

Rya*_*yan 11 sql-server recursion stored-procedures cursor

以下是我作为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
Run Code Online (Sandbox Code Playgroud)

无论如何将它转换为存储过程,因为它在子例程中得到了递归调用?

这是我试过的......

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
Run Code Online (Sandbox Code Playgroud)

但是,当调用该存储过程时,我收到以下错误...已存在具有相同名称"TableCursor"的游标.

Blo*_*ard 40

您可以指定LOCAL游标,如下所示:

DECLARE TableCursor CURSOR LOCAL FOR
SELECT ...
Run Code Online (Sandbox Code Playgroud)

至少在SQL Server 2008 R2(我的机器)中,这允许您递归调用sproc而不会遇到"Cursor already exists"错误.

  • 我也可以证实这也适用于2005年.http://msdn.microsoft.com/en-us/library/ms180169(v=sql.90).aspx这个答案应该真的标记为正确. (4认同)
  • 这解决了我的问题,非常感谢Blorgbeard (3认同)

Tob*_*oby 9

问题是,当您的游标不是全局游标时,它一个会话游标.由于您正在进行递归,即使每次迭代都在新的proc范围内创建游标,它们也会同时在同一个PID(连接)中创建,从而发生冲突.

您需要根据在递归期间不会再现的某些条件,在过程的每次迭代中生成唯一的游标名称.

或者,最好找到一种方法,使用set逻辑执行所需操作,并使用递归CTE处理任何必要的递归.

  • 生成唯一游标名称的最佳方法是什么?我对动态sql不太好. (3认同)