2010-06-28 40 views
9

Dưới đây là những gì tôi có như VBScript chương trình con:Là một thủ tục được lưu trữ được gọi là đệ quy có thể có trong 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 

Liệu có cách nào để tắt chức năng này thành một thủ tục lưu trữ từ nó có cuộc gọi đệ quy trong chương trình con?

Dưới đây là những gì tôi đã cố gắng ...

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 

Nhưng tôi nhận được lỗi sau khi mà thủ tục lưu trữ được gọi là ... Một con trỏ với cùng tên 'TableCursor' đã tồn tại.

+3

Tôi đoán lỗi của bạn xảy ra vì cuộc gọi đệ quy được thực hiện trước khi con trỏ 'Bảng con trỏ' bị đóng. Nó có thể cho con trỏ một tên động (có lẽ 'TableCursorN', trong đó N là chiều sâu của đệ quy - bạn phải làm cho nó như một tham số phụ)? – FrustratedWithFormsDesigner

+2

Vấn đề không phải là đệ quy, mà chắc chắn được cho phép (http://msdn.microsoft.com/en-us/library/aa175801 (SQL.80).aspx), đó là bạn đang sử dụng một tên con trỏ tĩnh. Tôi không biết đủ về con trỏ trong MS SQL Server để đăng bài này như là một câu trả lời, mặc dù, vì nó sẽ * có * để nói làm thế nào để sử dụng một con trỏ trong tình huống này là hữu ích! :-) –

Trả lời

7

Vấn đề là trong khi con trỏ của bạn không phải là toàn cầu, nó con trỏ phiên. Vì bạn đang thực hiện đệ quy, mặc dù mỗi lần lặp lại đang tạo một con trỏ trong một phạm vi proc mới, tất cả chúng đều được tạo ra trong cùng một PID (kết nối) cùng một lúc, do đó xung đột.

Bạn sẽ cần tạo tên con trỏ duy nhất trong mỗi lần lặp của quy trình dựa trên một số tiêu chí sẽ không được sao chép trong khi đệ quy.

Hoặc, tốt nhất là hãy tìm cách thực hiện những gì bạn cần bằng cách sử dụng logic đặt và xử lý bất kỳ đệ quy cần thiết nào bằng CTE đệ quy.

+3

Cách tốt nhất để tạo tên con trỏ duy nhất là gì? Tôi không quá tốt với sql động. – Ryan

2

Bạn có thể, nhưng thường không phải là một ý tưởng hay. SQL được thực hiện cho các hoạt động dựa trên thiết lập. Ngoài ra, trong MS SQL Server ít nhất, đệ quy được giới hạn số lượng các cuộc gọi đệ quy mà nó có thể thực hiện. Bạn chỉ có thể lồng sâu đến 32 cấp độ.

Sự cố trong trường hợp của bạn là CURSOR kéo dài qua từng cuộc gọi, vì vậy bạn sẽ tạo ra nhiều lần một lần.

34

Bạn có thể chỉ định một con trỏ LOCAL, như thế này:

DECLARE TableCursor CURSOR LOCAL FOR 
SELECT ... 

Ít nhất trong SQL Server 2008 R2 (máy của tôi), điều này cho phép bạn một cách đệ quy gọi sproc mà không cần chạy vào "Cursor đã tồn tại" lỗi .

+2

Điều này giải quyết được vấn đề của tôi nhờ có rất nhiều Blorgbeard – Javier

+4

Tôi có thể xác nhận điều này cũng hoạt động trong năm 2005. http://msdn.microsoft.com/en-us/library/ms180169(v=sql.90).aspx Câu trả lời này phải được đánh dấu là đúng. –

+1

Xác nhận lại –

Các vấn đề liên quan