Возможна ли рекурсивно называемая хранимая процедура в SQL Server?

вот что у меня есть как подпрограмма 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 ответов


проблема в том, что, хотя ваш курсор не является глобальным, он is курсор сессии. Поскольку вы делаете рекурсию, даже если каждая итерация создает курсор в новой области proc, все они создаются в одном и том же PID (соединении) одновременно, таким образом, столкновение.

вам нужно будет генерировать уникальные имена курсоров на каждой итерации процедуры на основе некоторых критериев, которые не будут воспроизводиться во время рекурсии.

Или, желательно, найти способ сделать то, что вам нужно, используя логику набора, и обрабатывать любую необходимую рекурсию с помощью рекурсивного CTE.


Вы можете указать LOCAL курсор, как это:

DECLARE TableCursor CURSOR LOCAL FOR
SELECT ...

по крайней мере, в SQL Server 2008 R2 (my machine) это позволяет рекурсивно вызывать sproc без запуска ошибок "курсор уже существует".


вы можете, но это обычно не очень хорошая идея. SQL-это сделано для операции. Кроме того, в MS SQL Server, по крайней мере, рекурсия ограничена количеством рекурсивных вызовов, которые она может сделать. Вы можете гнездиться только до 32 уровней.

проблема в вашем случае заключается в том, что курсор длится через каждый вызов, поэтому вы в конечном итоге создаете его более одного раза.