2010-06-28 101 views
9

這裏是我和VBScript子程序:在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 2010-06-28 17:24:58

+2

問題不是遞歸,肯定是允許的(http://msdn.microsoft.com/zh-cn/library/aa175801(SQL.80).aspx),這是您使用靜態遊標名稱。我對MS SQL Server中的遊標瞭解不夠,不過,因爲它會*說*在這種情況下如何使用遊標纔有用! :-) – 2010-06-28 17:25:18

回答

7

問題是,雖然你的光標不是全局的,但它一個會話遊標。既然你正在做遞歸,即使每次迭代都在一個新的proc範圍內創建一個遊標,它們都是在同一時間在同一個PID(連接)中創建的,因此也是碰撞。

根據一些在遞歸過程中不會被重現的標準,您需要在該過程的每次迭代中生成唯一的遊標名稱。

或者,最好是找到一種方法來使用設置邏輯來完成您所需的操作,並使用遞歸CTE處理任何必要的遞歸。

+3

生成唯一遊標名稱的最佳方法是什麼?動態sql我不太好。 – Ryan 2010-06-28 18:59:39

2

你可以,但通常不是一個好主意。 SQL用於基於集合的操作。此外,至少在MS SQL Server中,遞歸限於它可以進行的遞歸調用次數。您最多隻能嵌套32層。

在你的情況下,問題是CURSOR持續通過每次調用,所以你最終創建它不止一次。

34

你可以指定一個LOCAL光標,就像這樣:

DECLARE TableCursor CURSOR LOCAL FOR 
SELECT ... 

至少在SQL Server 2008 R2(我的機器),這可以讓你遞歸調用存儲過程,而不會在「光標已經存在」錯誤。

+2

這解決了我的問題,謝謝很多Blorgbeard – Javier 2012-06-05 15:22:38

+4

我也可以證實這個作品在2005年也是如此。 http://msdn.microsoft.com/en-us/library/ms180169(v=sql.90).aspx這個答案應該被標記爲正確的。 – 2013-04-04 11:26:22

+1

再次確認 – 2013-05-11 14:30:04

相關問題