2011-06-06 66 views
7

我在數據庫中有一個父子關係。我需要做的是循環父母的查詢,並使用父母的主鍵,得到它的孩子。我遇到的問題是我需要使用參數化遊標(傳入密鑰)來執行此操作。有沒有辦法在SQL Server光標中使用參數?

在SQL Server中是否存在這樣的事情或者一個模仿它的詭計?我試過這樣做,但它沒有工作:

DECLARE @value VARCHAR(20); 
DECLARE @someKey NUMERIC(19,0); 

DECLARE main_curs 
CURSOR FOR SELECT value FROM someTable where key = @someKey; 

SET @someKey = 12345; 

OPEN main_curs 
FETCH NEXT FROM main_curs INTO @value; 
CLOSE main_curs 
DEALLOCATE main_curs 

但它似乎沒有拿起我設置@someKey。

任何幫助,將不勝感激。謝謝!

UPDATE

我應該包括更多的信息,因爲我所做的例子似乎太簡單了。我有多個我需要使用的@someKey值。如前所述,我有一個親子關係,我最多可以有6個孩子。所以我得到了一份家長名單,它是各自的專欄,並通過它迭代。在WHILE-LOOP中,我想從父項中獲取主鍵並調用另一個光標來獲取子項信息(返回不同的列)。所以我會用不同的@someKey值設置多個對子游標的調用。希望這是有道理的。

+0

你可以只是查詢數據,而不使用選擇這通常更有效嗎? – Mark 2012-10-12 19:33:48

回答

0

你可以嘗試的一件事是使用nested cursors。一個例子是標題爲:的頁面底部使用嵌套遊標生成報告輸出

0

需要之前設置@someKey = 12345;遊標聲明,如:

SET @someKey = 12345; 
DECLARE main_curs 
CURSOR FOR SELECT value FROM someTable where key = @someKey; 
+0

我試過這個,發現如果關閉遊標,改變變量的值,然後重新打開遊標,遊標的內容不會改變。看起來,遊標定義是在DECLARE語句中「編譯」的。我來到這裏是爲了尋找解決光標問題的方法。 – 2016-07-25 20:37:40

0

你似乎有東西錯了順序,你沒有真正做光標裏面什麼?

DECLARE @value VARCHAR(20); 
DECLARE @someKey NUMERIC(19,0); 

SET @someKey = 12345; --this has to be set before its used in cursor declaration 

DECLARE main_curs 
CURSOR FOR SELECT value FROM someTable where key = @someKey; 
OPEN main_curs 
FETCH NEXT FROM main_curs INTO @value;  -- first row is fetched 

WHILE @@FETCH_STATUS = 0  -- start the loop 
BEGIN 

-- do something here with @value 

FETCH NEXT FROM main_curs INTO @value; --fetch the next row 
END 

CLOSE main_curs 
DEALLOCATE main_curs 
0

如果您想遍歷遞歸層次使用CTE,請參閱Recursive Queries Using Common Table Expressions。您可以在遞歸CTE上聲明您的光標,例如:

create table test (
    id int not null identity(1,1) primary key, 
    parent_id int null, 
    data varchar (max)); 
go 

insert into test (parent_id, data) values 
    (null, 'root'), 
    (1, 'child 1'), 
    (1, 'child 2') , 
    (2, 'child of child 1'), 
    (4, 'child of child of child 1'); 
go 

declare @root int = 2; 

declare crs cursor for 
    with cte as (
     select id, parent_id, data 
     from test 
     where id = @root 
     union all 
     select t.id, t.parent_id, t.data 
     from test t 
      join cte c on t.parent_id = c.id) 
    select id, data from cte;  
open crs; 

declare @id int, @data varchar(max); 
fetch next from crs into @id, @data; 
while @@fetch_status = 0 
begin 
    print @data; 
    fetch next from crs into @id, @data; 
end 

close crs; 
deallocate crs; 

但是大多數情況下,遞歸CTE可以完全消除對遊標的需要。

4

你需要的是2個遊標 - 一個用於父級,另一個用於子級。確保子游標在LOOP內部不被DECLARED聲明,如果你聲明在外部,它將不起作用。

如:

DECLARE @value VARCHAR(20); 
DECLARE @someKey NUMERIC(19,0); 

DECLARE main_curs 
CURSOR FOR SELECT value FROM someTable where key = @someKey; 

SET @someKey = 12345; 

OPEN main_curs 
FETCH NEXT FROM main_curs INTO @value; 
while @@FETCH_STATUS = 0 
BEGIN 

DECLARE CHILD_CURS CURSOR FOR SELECT VALUE2 FROM CHILDTABLE WHERE [email protected]; 
open child_curs 
fetch next from child_curs into @x,@y 

close child_curs 
deallocate child_curs 

FETCH NEXT FROM main_curs INTO @value; 
END 

CLOSE main_curs 
DEALLOCATE main_curs 
+0

這適用於@someKey的單個值,但正如Karl Hoaglund在關於此解決方案的另一個答案的評論中所述,「我嘗試了這一步,發現如果關閉遊標,遊標的內容不會更改,請更改值在變量中,然後重新打開遊標,看起來遊標定義是在DECLARE語句中「編譯」的,我來到這裏是爲了找到一個解除遊標的解決方法。這使得我們三個人在尋找答案,包括在OP中的更新中的Ascalonian。 – 2016-12-12 14:48:58

+0

@Rob - 根據John Kane發佈的有關嵌套遊標的Microsoft幫助文檔鏈接中的信息,您的回覆有些不準確。儘管關閉子游標確實不會改變內容,但釋放子游標將強制SQL在循環的下一次迭代中重新創建遊標(從而更改內容)。所以卡爾是誤導性的,這篇文章也有誤導性。您可以通過在循環內釋放內部子游標來執行預期的操作,而不僅僅是關閉它。所以這個例子是準確的。 – Mitselplik 2017-02-13 22:22:59

0

在另外一個地方,一個人在使用存儲過程(編譯SQL,而不是一個特設的腳本)建議,但不工作的。這裏的另一個MWE,顯示問題相當明確:

/* Should print: 
dbNamein=master dbNameout=master 
dbNamein=model dbNameout=model 
dbNamein=msdb dbNameout=msdb 
*/ 
create procedure [TestParamsWithOpenCursorStmt] 
as 
begin 

    declare @dbNameIn [nvarchar](255) = N'tempdb', 
     @dbNameOut [nvarchar](255), 
     @fs [int]; 
    declare dbNames cursor for 
     select db.[name] from [master].[sys].[databases] db 
     where db.[name] = @dbNameIn; 
    while (@dbNameIn != N'msdb') begin 
     if @dbNameIn = N'tempdb' 
     set @dbNameIn = N'master' 
     else if @dbNameIn = N'master' 
     set @dbNameIn = N'model' 
     else if @dbNameIn = N'model' 
     set @dbNameIn = N'msdb'; 
     open dbNames; 
     fetch next from dbNames into @dbNameOut; 
     set @fs = @@fetch_status; 
     if @fs != 0 continue; 
     raiserror (N'dbNamein=%s dbNameout=%s', 0, 0, @dbNameIn, @dbNameOut) with nowait; 
     close dbNames; 
    end; 
    deallocate dbNames; 

end; 
go 

execute [TestParamsWithOpenCursorStmt]; 

看來,變量(和它的時間價值)被綁定到「申報...光標」,而不是打開的遊標。

1

下面介紹如何使用'EXEC()'函數聲明帶有動態SQL的遊標。令人驚訝的是,這確實奏效。例如:

DECLARE @QuotedDatabase NVARCHAR(128) = QUOTENAME('ReportServer') 
DECLARE @ObjectID INT = 389576426 
DECLARE @ColumnName NVARCHAR(128) 
DECLARE @ColumnType NVARCHAR(128) 
DECLARE @DeclareColumnCursor NVARCHAR(4000) 
SET @DeclareColumnCursor = ' 
    DECLARE ColumnCursor CURSOR READ_ONLY FORWARD_ONLY FOR 
    SELECT c.Name, t.Name 
    FROM ' + @QuotedDatabase + '.sys.columns c 
    INNER JOIN ' + @QuotedDatabase + '.sys.types t 
    ON c.user_type_id = t.user_type_id 
    WHERE c.object_id = ' + CAST(@ObjectID AS NVARCHAR) + ' 
    ORDER BY column_id' 
EXEC(@DeclareColumnCursor) 
OPEN ColumnCursor 
FETCH NEXT FROM ColumnCursor INTO @ColumnName, @ColumnType 
PRINT @ColumnName + ',' + @ColumnType 
CLOSE ColumnCursor 
DEALLOCATE ColumnCursor 
相關問題