Возможна ли рекурсивно называемая хранимая процедура в 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 уровней.
проблема в вашем случае заключается в том, что курсор длится через каждый вызов, поэтому вы в конечном итоге создаете его более одного раза.