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

发布于 2024-09-07 04:21:22 字数 2269 浏览 9 评论 0原文

这是我的 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”的游标已存在。

Here is what I have as VBScript Subroutine:

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

Is there anyway to turn this into a stored procedure since it's got the recursive call in the subroutine?

Here is what I've tried...

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

But I get the following error when that stored procedure is called...

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烟凡古楼 2024-09-14 04:21:22

您可以指定 LOCAL 光标,例如this:

DECLARE TableCursor CURSOR LOCAL FOR
SELECT ...

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

You can specify a LOCAL cursor, like this:

DECLARE TableCursor CURSOR LOCAL FOR
SELECT ...

At least in SQL Server 2008 R2 (my machine), this allows you to recursively call the sproc without running into "Cursor already exists" errors.

一枫情书 2024-09-14 04:21:22

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

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

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

The problem is that while your cursor isn't global, it is a session cursor. Since you're doing recursion, even though each iteration is creating a cursor in a new proc scope, they're all being created in the same PID (connection) at the same time, thus the collision.

You'll need to generate unique cursor names in each iteration of the procedure based on some criteria that won't be reproduced during the recursion.

Or, preferably, find a way to do what you need using set logic, and handle any necessary recursion using a recursive CTE.

倾城泪 2024-09-14 04:21:22

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

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

You can, but it's usually not a good idea. SQL is made for set-based operations. Also, in MS SQL Server at least, the recursion is limited to the number of recursive calls that it can make. You can only nest up to 32 levels deep.

The problem in your case is that the CURSOR lasts through each call, so you end up creating it more than once.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文