80 votes

Puis-je boucler sur une variable de table en T-SQL ?

Existe-t-il un moyen de boucler une variable de table en T-SQL ?

DECLARE @table1 TABLE ( col1 int )  
INSERT into @table1 SELECT col1 FROM table2

J'utilise également des curseurs, mais ceux-ci semblent moins flexibles que les variables de table.

DECLARE cursor1 CURSOR  
    FOR SELECT col1 FROM table2  
OPEN cursor1  
FETCH NEXT FROM cursor1

J'aimerais pouvoir utiliser une variable de table de la même manière qu'un curseur. Ainsi, je pourrais exécuter une requête sur la variable de table dans une partie de la procédure, puis plus tard exécuter un code pour chaque ligne de la variable de table.

Toute aide est la bienvenue.

0 votes

Question similaire ici : stackoverflow.com/questions/61967/

3 votes

"Les curseurs semblent moins flexibles que les variables de table". Cette affirmation n'a pas vraiment de sens. Ce sont des choses totalement différentes. Vous pouvez certainement utiliser un curseur pour itérer dans une variable de table.

0 votes

124voto

KM. Points 51800

Ajoutez une identité à votre variable de table, et faites une boucle simple de 1 à l'@@ROWCOUNT de l'INSERT-SELECT.

Essayez ça :

DECLARE @RowsToProcess  int
DECLARE @CurrentRow     int
DECLARE @SelectCol1     int

DECLARE @table1 TABLE (RowID int not null primary key identity(1,1), col1 int )  
INSERT into @table1 (col1) SELECT col1 FROM table2
SET @RowsToProcess=@@ROWCOUNT

SET @CurrentRow=0
WHILE @CurrentRow<@RowsToProcess
BEGIN
    SET @CurrentRow=@CurrentRow+1
    SELECT 
        @SelectCol1=col1
        FROM @table1
        WHERE RowID=@CurrentRow

    --do your thing here--

END

5 votes

Cela semble être le plus simple du lot. Merci.

18voto

Justin Niessner Points 144953
DECLARE @table1 TABLE (
    idx int identity(1,1),
    col1 int )

DECLARE @counter int

SET @counter = 1

WHILE(@counter < SELECT MAX(idx) FROM @table1)
BEGIN
    DECLARE @colVar INT

    SELECT @colVar = col1 FROM @table1 WHERE idx = @counter

    -- Do your work here

    SET @counter = @counter + 1
END

Croyez-le ou non, cette méthode est en fait plus efficace et plus performante que l'utilisation d'un curseur.

0 votes

Pourquoi sélectionner le maximum à chaque fois dans la boucle ?

1 votes

Vous pouviez le sélectionner une fois et le stocker dans une variable assez facilement... c'était juste quelques touches de moins.

1 votes

Pourquoi sélectionner le maximum à chaque fois dans la boucle ? Par conséquent, vous devez toucher la variable de la table deux fois par itération. Vous pourriez supprimer la SELECT MAX() dans le WHILE() si vous saisissez le @@ROWCOUNT de la population variable de la table, comme je le fais dans ma réponse.

6voto

Raj More Points 22358

Vous pouvez boucler la variable de la table ou vous pouvez la parcourir avec un curseur. C'est ce que nous appelons habituellement un RBAR - prononcé Reebar et signifiant Row-By-Agonizing-Row.

Je vous suggère de trouver une réponse à votre question basée sur le SET (nous pouvons vous y aider) et de vous éloigner autant que possible des rbars.

0 votes

C'est en fait la raison pour laquelle je veux utiliser une variable de table au lieu d'un curseur. Je cherche généralement un moyen d'obtenir le résultat souhaité en utilisant un JOIN sur une variable de table, mais si je ne trouve pas de moyen d'utiliser un JOIN, je peux alors me rabattre sur une boucle sur cette même variable de table. Mais je suis d'accord pour dire que la méthode des ensembles est la meilleure.

0 votes

Le bouclage sur une variable de table n'est pas meilleur qu'un curseur. En fait, cela peut même être pire. Le seul avantage réel de changer le code des curseurs en boucles est le "droit de se vanter". Ex : "Je n'ai pas de curseurs dans mon code".

6voto

Gildor Points 61

Ressemble à cette démo :

DECLARE @vTable TABLE (IdRow int not null primary key identity(1,1),ValueRow int);

-------Initialize---------
insert into @vTable select 345;
insert into @vTable select 795;
insert into @vTable select 565;
---------------------------

DECLARE @cnt int = 1;
DECLARE @max int = (SELECT MAX(IdRow) FROM @vTable);

WHILE @cnt <= @max
BEGIN
    DECLARE @tempValueRow int = (Select ValueRow FROM @vTable WHERE IdRow = @cnt);

    ---work demo----
    print '@tempValueRow:' + convert(varchar(10),@tempValueRow);
    print '@cnt:' + convert(varchar(10),@cnt);
    print'';
    --------------

    set @cnt = @cnt+1;
END

Version sans idRow, utilisant ROW_NUMBER

    DECLARE @vTable TABLE (ValueRow int);
-------Initialize---------
insert into @vTable select 345;
insert into @vTable select 795;
insert into @vTable select 565;
---------------------------

DECLARE @cnt int = 1;
DECLARE @max int = (select count(*) from @vTable);

WHILE @cnt <= @max
BEGIN
    DECLARE @tempValueRow int = (
        select ValueRow 
        from (select ValueRow
            , ROW_NUMBER() OVER(ORDER BY (select 1)) as RowId 
            from @vTable
        ) T1 
    where t1.RowId = @cnt
    );

    ---work demo----
    print '@tempValueRow:' + convert(varchar(10),@tempValueRow);
    print '@cnt:' + convert(varchar(10),@cnt);
    print'';
    --------------

    set @cnt = @cnt+1;
END

3voto

Jeff Meatball Yang Points 12021

Voici une autre réponse, similaire à celle de Justin, mais qui ne nécessite pas d'identité ou d'agrégat, juste une clé primaire (unique).

declare @table1 table(dataKey int, dataCol1 varchar(20), dataCol2 datetime)
declare @dataKey int
while exists select 'x' from @table1
begin
    select top 1 @dataKey = dataKey 
    from @table1 
    order by /*whatever you want:*/ dataCol2 desc

    -- do processing

    delete from @table1 where dataKey = @dataKey
end

0 votes

À chaque itération, vous touchez la variable table 3 fois, ce qui ne peut pas être très efficace

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X