Есть ли способ использовать параметры в курсоре SQL Server? - PullRequest
7 голосов
/ 06 июня 2011

У меня есть отношения родитель-ребенок в базе данных. Что мне нужно сделать, так это перебрать запрос родителя и, используя первичный ключ родителя, получить его дочерние элементы. У меня проблема в том, что для этого мне нужно использовать параметризованный курсор (передать ключ).

Есть ли такая вещь в 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. Надеюсь, что это имеет смысл.

Ответы [ 7 ]

6 голосов
/ 12 октября 2012

Вам нужны 2 курсора - один для родительского и один для дочернего. Убедитесь, что дочерний курсор объявлен внутри LOOP, а не снаружи. Он не будет работать, если вы объявите снаружи.

Например:

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 value=@value;
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
1 голос
/ 17 июля 2017

Вот как вы можете объявить курсор с помощью динамического SQL, используя функцию EXEC (). Удивительно, но это работает. Например:

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
1 голос
/ 06 июня 2011

Вы можете попробовать использовать вложенные курсоры .Пример этого - внизу страницы под названием: Использование вложенных курсоров для вывода отчета .

0 голосов
/ 12 декабря 2016

В другом месте кто-то предложил использовать хранимую процедуру (скомпилированный 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];

Похоже, что переменная (и ее значение в то время) привязана к «объявить ... курсор», а не к открытому курсору.

0 голосов
/ 06 июня 2011

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

0 голосов
/ 06 июня 2011

Кажется, у вас неправильный порядок вещей, и вы на самом деле ничего не делаете внутри курсора?

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 голосов
/ 06 июня 2011

вам нужно установить @someKey = 12345; до объявления курсора, например:

SET @someKey = 12345;
DECLARE main_curs 
CURSOR FOR SELECT value FROM someTable where key = @someKey;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...