web-dev-qa-db-fra.com

Puis-je parcourir une variable de table dans T-SQL?

Est-il possible de parcourir une variable de table dans T-SQL?

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

J'utilise aussi les curseurs, mais les curseurs 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 exécuter ultérieurement du code pour chaque ligne de la variable de table.

Toute aide est grandement appréciée.

63
Kuyenda

Ajoutez une identité à votre variable de table et faites une boucle facile de 1 à @@ ROWCOUNT de INSERT-SELECT.

Essaye ç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
91
KM.
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, c'est en fait plus efficace et performant que d'utiliser un curseur.

14
Justin Niessner

Vous pouvez parcourir la variable de table ou le faire défiler. C'est ce que nous appelons habituellement un RBAR - prononcé Reebar et signifie Row-By-Agonizing-Row.

Je suggérerais de trouver une réponse FIXÉE à votre question (nous pouvons vous aider) et de vous éloigner le plus possible de la barre.

6
Raj More

Mes deux sous .. De la réponse de KM., Si vous voulez supprimer une variable, vous pouvez faire un compte à rebours sur @RowsToProcess au lieu de compter.

DECLARE @RowsToProcess  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 

WHILE @RowsToProcess > 0 -- Countdown
BEGIN
    SELECT *
        FROM @table1
        WHERE RowID=@RowsToProcess

    --do your thing here--

    SET @RowsToProcess = @RowsToProcess - 1; -- Countdown
END
6
typoerrpr

ressembler à 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, en 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
5
Gildor

Voici une autre réponse, semblable à celle de Justin, mais qui n’a pas besoin d’une identité ou d’un agrégat, mais simplement d’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
2
Jeff Meatball Yang

Je ne connaissais pas la structure WHILE.

La structure WHILE avec une variable de table ressemble toutefois à l'utilisation d'un CURSEUR, en ce sens qu'il vous reste à sélectionner la ligne dans une variable basée sur la ligne IDENTITY, qui est en réalité un FETCH.

Existe-t-il une différence entre l’utilisation de WHERE et les éléments suivants?

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

DECLARE cursor1 CURSOR  
    FOR @table1
OPEN cursor1  
FETCH NEXT FROM cursor1

Je ne sais pas si c'est même possible. Je suppose que vous pourriez avoir à faire ceci:

DECLARE cursor1 CURSOR  
    FOR SELECT col1 FROM @table1
OPEN cursor1  
FETCH NEXT FROM cursor1

Merci pour ton aide!

2
Kuyenda

Voici ma variante. Presque comme tous les autres, mais je n’utilise qu’une seule variable pour gérer le bouclage.

DECLARE
  @LoopId  int
 ,@MyData  varchar(100)

DECLARE @CheckThese TABLE
 (
   LoopId  int  not null  identity(1,1)
  ,MyData  varchar(100)  not null
 )


INSERT @CheckThese (YourData)
 select MyData from MyTable
 order by DoesItMatter

SET @LoopId = @@rowcount

WHILE @LoopId > 0
 BEGIN
    SELECT @MyData = MyData
     from @CheckThese
     where LoopId = @LoopId

    --  Do whatever

    SET @LoopId = @LoopId - 1
 END

Le point de Raj More est pertinent - effectuez des boucles uniquement si vous devez le faire.

2
Philip Kelley

Voici ma version de la même solution ...

    declare @id int

        SELECT @id = min(fPat.PatientID)
        FROM tbPatients fPat
        WHERE (fPat.InsNotes is not null AND DataLength(fPat.InsNotes)>0)

while @id is not null
begin
    SELECT fPat.PatientID, fPat.InsNotes
    FROM tbPatients fPat
    WHERE (fPat.InsNotes is not null AND DataLength(fPat.InsNotes)>0) AND fPat.PatientID=@id

    SELECT @id = min(fPat.PatientID)
    FROM tbPatients fPat
    WHERE (fPat.InsNotes is not null AND DataLength(fPat.InsNotes)>0)AND fPat.PatientID>@id

end
1
Gary Kindel

Après la procédure stockée, parcourez la variable de table et l’imprimez dans l’ordre croissant. Cet exemple utilise WHILE LOOP. 

CREATE PROCEDURE PrintSequenceSeries 
    -- Add the parameters for the stored procedure here
    @ComaSeperatedSequenceSeries nVarchar(MAX)  
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @SERIES_COUNT AS INTEGER
    SELECT @SERIES_COUNT = COUNT(*) FROM PARSE_COMMA_DELIMITED_INTEGER(@ComaSeperatedSequenceSeries, ',')  --- ORDER BY ITEM DESC

    DECLARE @CURR_COUNT AS INTEGER
    SET @CURR_COUNT = 1

    DECLARE @SQL AS NVARCHAR(MAX)

    WHILE @CURR_COUNT <= @SERIES_COUNT
    BEGIN
        SET @SQL = 'SELECT TOP 1 T.* FROM ' + 
            '(SELECT TOP ' + CONVERT(VARCHAR(20), @CURR_COUNT) + ' * FROM PARSE_COMMA_DELIMITED_INTEGER( ''' + @ComaSeperatedSequenceSeries + ''' , '','') ORDER BY ITEM ASC) AS T ' +
            'ORDER BY T.ITEM DESC '
        PRINT @SQL 
        EXEC SP_EXECUTESQL @SQL 
        SET @CURR_COUNT = @CURR_COUNT + 1
    END;

La déclaration suivante exécute la procédure stockée:

EXEC  PrintSequenceSeries '11,2,33,14,5,60,17,98,9,10'

Le résultat affiché dans la fenêtre de requête SQL est présenté ci-dessous:

 The Result of PrintSequenceSeries

La fonction PARSE_COMMA_DELIMITED_INTEGER () qui renvoie la variable TABLE est comme ci-dessous:

CREATE FUNCTION [dbo].[parse_comma_delimited_integer]
        (
            @LIST       VARCHAR(8000), 
            @DELIMITER  VARCHAR(10) = ',
            '
        )

        -- TABLE VARIABLE THAT WILL CONTAIN VALUES
        RETURNS @TABLEVALUES TABLE 
        (
            ITEM INT
        )
        AS
        BEGIN 
            DECLARE @ITEM VARCHAR(255)

            /* LOOP OVER THE COMMADELIMITED LIST */
            WHILE (DATALENGTH(@LIST) > 0)
                BEGIN 
                    IF CHARINDEX(@DELIMITER,@LIST) > 0
                        BEGIN
                            SELECT @ITEM = SUBSTRING(@LIST,1,(CHARINDEX(@DELIMITER, @LIST)-1))
                            SELECT @LIST =  SUBSTRING(@LIST,(CHARINDEX(@DELIMITER, @LIST) +
                            DATALENGTH(@DELIMITER)),DATALENGTH(@LIST))
                        END
                    ELSE
                        BEGIN
                            SELECT @ITEM = @LIST
                            SELECT @LIST = NULL
                        END

                    -- INSERT EACH ITEM INTO TEMP TABLE
                    INSERT @TABLEVALUES 
                    (
                        ITEM
                    )
                    SELECT ITEM = CONVERT(INT, @ITEM) 
                END
        RETURN
        END
0
Rahul Varadkar