2009-10-16 155 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 
相關問題