【问题标题】:Is a recursively called stored procedure possible in SQL Server?SQL Server 中是否可以递归调用存储过程?
【发布时间】:2011-03-09 06:07:38
【问题描述】:

这是我所拥有的 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.

【问题讨论】:

  • 我猜你的错误是因为在光标TableCursor 关闭之前进行了递归调用。是否可以给光标一个动态名称(可能是TableCursorN,其中 N 是递归的深度 - 您必须将其作为额外参数)?
  • 问题不在于递归,这当然是允许的(msdn.microsoft.com/en-us/library/aa175801(SQL.80).aspx),而是您使用的是静态游标名称。不过,我对 MS SQL Server 中的游标知之甚少,无法将其发布为答案,因为它会 必须 说明如何在这种情况下使用游标才有用! :-)

标签: sql-server recursion stored-procedures database-cursor


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 生成唯一游标名称的最佳方法是什么?我不太擅长动态sql。
    【解决方案3】:

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

    DECLARE TableCursor CURSOR LOCAL FOR
    SELECT ...
    

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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-16
    • 1970-01-01
    • 2021-04-13
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    相关资源
    最近更新 更多