2009-10-16 148 views
56

无论如何循环T-SQL中的表变量?我可以循环访问T-SQL中的表变量吗?

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

我也使用游标,但游标似乎不如表变量灵活。

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

我希望能够以与游标相同的方式使用表变量。这样我就可以在过程的一部分中对表变量执行一些查询,然后稍后为表变量中的每一行执行一些代码。

任何帮助,非常感谢。

+0

类似的问题在这里:http://stackoverflow.com/questions/61967/is-there-a-way-to-loop-through-a-table-variable-in-tsql-without-using-a-光标 – demp 2013-01-10 11:10:08

+2

“游标看起来不如表变量灵活”。这个陈述并不合理。他们是完全不同的东西。您当然可以使用游标来遍历表变量。 – 2013-10-20 18:43:58

回答

79

为您的表变量添加一个标识,并从1到INSERT-SELECT的@@ ROWCOUNT执行一个简单的循环。

试试这个:

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 @[email protected]@ROWCOUNT 

SET @CurrentRow=0 
WHILE @CurrentRow<@RowsToProcess 
BEGIN 
    SET @[email protected]+1 
    SELECT 
     @SelectCol1=col1 
     FROM @table1 
     WHERE [email protected] 

    --do your thing here-- 

END 
+4

这似乎是很多最简单的。谢谢! – Kuyenda 2009-10-16 14:45:54

12
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 

相信与否,这实际上比使用游标更有效率和更高效。

+0

为什么每次在循环中选择最大值? – 2009-10-16 14:03:35

+0

您可以选择一次并将其存储在一个变量中,这只是简短的几个按键。 – 2009-10-16 14:13:06

+1

为什么每次在循环中选择最大值?结果,你必须在每次迭代中选择两次表格变量。如果从表变量族中捕获@@ ROWCOUNT,就可以在WHILE()中删除SELECT MAX(),就像我在答案中所做的那样。 – 2009-10-16 14:13:28

6

您可以遍历表变量,或者您可以通过它遍历光标。这就是我们通常所说的RBAR - 发音Reebar并且意味着行逐行化。

我会建议找到一个基于集合的答案来解决你的问题(我们可以帮助解决这个问题)并尽可能远离rbars。

+0

这实际上是我为什么要使用表变量而不是游标。我通常寻找一种方法来使用表变量上的JOIN来获得预期的结果,但是如果我找不到使用JOIN的方法,那么我可以回退到同一个表变量上的循环。但我同意,基于集合是最好的。 – Kuyenda 2009-10-16 14:16:59

+0

在表变量上循环并不比游标更好。事实上,它实际上可能更糟糕。将代码从游标更改为循环的唯一好处是“吹牛权”。例如:“我的代码中没有任何游标”。 – 2013-06-06 15:13:00

2

下面是与Justin类似的另一个答案,但不需要身份或集合,只是主要(唯一)键。

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

每次迭代你打表变量3次,这不可能是有效的 – 2009-10-16 14:10:49

2

这里是我的变种。几乎和所有其他人一样,但我只使用一个变量来管理循环。

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 

拉杰更多的观点是相关的 - 只有在必要时才执行循环。

2

我不知道WHILE结构。

但是,带有表变量的WHILE结构看起来类似于使用CURSOR,因为您仍然必须根据行IDENTITY将行选择到变量中,该行实际上是一个FETCH。

使用WHERE和类似下面的东西有什么区别吗?

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

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

我不知道这是否可能。我想你可能不得不这样做:

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

感谢您的帮助!

+1

你的代码:_DECLARE cursor1 CURSOR FOR @ table1 OPEN cursor1_将不起作用。游标必须在其定义中有一个SELECT,就像第二个代码示例一样。如果你做了一些测试,你会发现不使用游标循环比使用游标循环更快。 – 2009-10-16 18:05:47

1

这是我的版本相同的解决方案...

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 [email protected] 

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

end 
5

这个样子的演示:

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 

版本不idRow,使用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

我的两分钱。从KM的回答,如果你想删除一个。变量,您可以在@RowsToProcess上进行倒计时而不是计数。

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 [email protected] 

    --do your thing here-- 

    SET @RowsToProcess = @RowsToProcess - 1; -- Countdown 
END 
+0

这是一个更好的解决方案,因为它不依赖于表变量的内容。 – beerwin 2015-10-15 08:17:04

0

以下存储过程循环遍历表变量并以升序排列打印。这个例子使用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; 

下面的语句执行存储过程:

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

在SQL查询窗口中显示的结果如下所示:

The Result of PrintSequenceSeries

函数PARSE_COMMA_DELIMITED_INTEGER()返回TABLE变量如下所示:

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 
相关问题