2010-06-28 2 views
9

Вот что у меня есть, как VBScript Subroutine:Является ли рекурсивно названная хранимая процедура возможной в SQL Server?

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

Я предполагаю, что ваша ошибка возникает из-за того, что рекурсивный вызов выполняется до того, как курсор 'TableCursor' закрыт. Можно ли дать курсору динамическое имя (возможно, «TableCursorN», где N - глубина рекурсии - вам нужно сделать это как дополнительный параметр)? – FrustratedWithFormsDesigner

+2

Проблема заключается не в рекурсии, которая, безусловно, разрешена (http://msdn.microsoft.com/en-us/library/aa175801(SQL.80).aspx), это то, что вы используете статическое имя курсора. Я не знаю достаточно о курсорах в MS SQL Server, чтобы опубликовать это как ответ, хотя, поскольку он будет * иметь * сказать, как использовать курсор в этой ситуации, чтобы быть полезным! :-) –

ответ

7

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

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

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

+3

Каков наилучший способ создания уникальных имен курсоров? Я не слишком хорош с динамическим sql. – Ryan

2

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

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

34

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

DECLARE TableCursor CURSOR LOCAL FOR 
SELECT ... 

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

+2

Это решило мою проблему, спасибо большое Blorgbeard – Javier

+4

Я тоже могу подтвердить это на 2005 год. http://msdn.microsoft.com/en-us/library/ms180169(v=sql.90).aspx Этот ответ должен быть помечен как правильный. –

+1

Подтверждено снова –

Смежные вопросы