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 ...

至少在 SQL Server 2008 R2(我的机器)中,这允许您递归调用存储过程而不会遇到“光标已存在”错误。

于 2012-04-26T04:05:37.200 回答
11

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

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

或者,最好找到一种方法来使用集合逻辑来做你需要的事情,并使用递归 CTE 处理任何必要的递归。

于 2010-06-28T17:28:50.317 回答
2

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

您的问题是 CURSOR 在每次调用中都持续存在,因此您最终会多次创建它。

于 2010-06-28T17:26:54.093 回答