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

时间:2010-06-28 17:20:13

标签: sql-server stored-procedures recursion 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

是否有将其转换为存储过程,因为它在子例程中进行了递归调用?

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

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

但是在调用该存储过程时出现以下错误... 已存在同名“TableCursor”的游标。

3 个答案:

答案 0 :(得分:37)

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

DECLARE TableCursor CURSOR LOCAL FOR
SELECT ...

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

答案 1 :(得分:8)

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

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

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

答案 2 :(得分:2)

你可以,但通常不是一个好主意。 SQL用于基于集合的操作。此外,至少在MS SQL Server中,递归仅限于它可以进行的递归调用的数量。您最多只能嵌套32个级别。

你的案例中的问题是CURSOR会持续通过每次调用,因此你最终会多次创建它。