7
votes

Existe-t-il un moyen d'utiliser des paramètres dans un curseur SQL Server?

J'ai une relation parent-enfant dans la base de données. Ce que je dois faire, c'est une boucle à travers la requête du parent et utiliser la clé principale du parent, obtenez ses enfants. Le problème que j'ai eu est que je dois utiliser un curseur paramétré (passer dans la clé) pour le faire.

Y a-t-il une telle chose dans SQL Server ou un tour pour imiter cela? J'ai essayé de faire ça, mais cela n'a pas fonctionné: P>

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


1 commentaires

Pouvez-vous simplement interroger pour les données et ne pas utiliser de sélectionner cela est généralement plus efficace?


7 Réponses :


0
votes

Vous devez définir @somekey = 12345; code> avant la déclaration du curseur tel que:

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


1 commentaires

J'ai essayé cela et j'ai constaté que le contenu du curseur ne change pas si vous fermez le curseur, modifiez la valeur dans la variable, puis rouvrez le curseur. Il semble que la définition du curseur soit "compilée" dans la déclaration de déclaration. Je suis venu ici pour chercher un travail autour d'un peu de redécoupage du curseur.



0
votes

Vous semblez avoir l'ordre des choses qui ne va pas, et vous ne faites rien dans le curseur?

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 commentaires

1
votes

Une chose que vous pouvez essayer consiste à utiliser curseurs imbriqués . Un exemple de ceci est au bas de la page intitulée: à l'aide de curseurs imbriqués pour produire une sortie de rapport .


0 commentaires

0
votes

Si vous souhaitez itérer sur une hiérarchie récursive, utilisez des CTES, voir requêtes récursives à l'aide de Expressions de table communes . Vous pouvez déclarer votre curseur sur le CTE récursif, par exemple:

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;


0 commentaires

6
votes

Ce dont vous avez besoin est de 2 curseurs - un pour le parent et un pour l'enfant. Make Assurez-vous que le curseur enfant est déclaré à l'intérieur de la boucle, pas à l'extérieur.La ne fonctionnera pas si vous déclarez dehors.

par exemple: P> >

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


2 commentaires

Cela fonctionne pour une valeur unique de @somekey, mais comme le dit Karl Hoaglund dans un commentaire d'une autre réponse concernant cette solution, "j'ai essayé cela et j'ai constaté que le contenu du curseur ne change pas si vous fermez le curseur, changez la valeur La variable, puis rouvrez le curseur. Il apparaît que la définition du curseur est "compilée" dans la déclaration de déclaration. Je suis venu ici pour chercher un travail autour de la redécration du curseur. " Ce qui nous donne trois à la recherche de cette réponse, y compris une ascalonienne par la mise à jour de l'OP.


@ROB - selon les informations du lien vers la documentation d'aide Microsoft sur des curseurs imbriqués Publié par John Kane, votre réponse est quelque peu inexacte. Bien qu'il soit vrai que la fermeture du curseur enfant ne change pas le contenu, le curseur d'enfant empêche le SQL pour recréer le curseur (et modifier ainsi le contenu) sur la prochaine itération de la boucle. Alors Karl est trompeur aussi bien que ce post est trompeur. Vous pouvez obtenir le curseur de l'enfant intérieur pour fonctionner comme prévu en le déroulant dans la boucle, non seulement la fermeture. Donc, cet exemple est précis.



1
votes

Dans un autre endroit, une personne a suggéré d'utiliser une procédure stockée (compilée SQL plutôt qu'un script ad-hoc) mais cela ne fonctionne pas non plus. Voici une autre MWe qui montre le problème assez clairement:

/* 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 commentaires

1
votes

Voici comment vous pouvez déclarer un curseur avec SQL dynamique, à l'aide de la fonction 'Exec ()'. Étonnamment cela fonctionne. Par exemple: xxx


0 commentaires