2009-10-16 20 views
56

Có cách nào để lặp qua biến bảng trong T-SQL không?Tôi có thể lặp qua biến bảng trong T-SQL không?

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

Tôi cũng sử dụng con trỏ, nhưng con trỏ dường như kém linh hoạt hơn biến bảng.

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

Tôi muốn có thể sử dụng biến bảng theo cách tương tự như con trỏ. Bằng cách đó tôi có thể thực hiện một số truy vấn trên biến bảng trong một phần của thủ tục, và sau đó thực hiện một số mã cho mỗi hàng trong biến bảng.

Bất kỳ trợ giúp nào được đánh giá cao.

+0

câu hỏi tương tự tại đây: http://stackoverflow.com/questions/61967/is-there-a-way-to-loop-through-a-table-variable-in-tsql-without-using-a- con trỏ – demp

+2

"con trỏ dường như kém linh hoạt hơn so với biến bảng". Tuyên bố này không thực sự có ý nghĩa. Chúng hoàn toàn khác nhau. Bạn chắc chắn có thể sử dụng một con trỏ để lặp qua một biến bảng. –

Trả lời

79

Thêm một sắc để biến bảng của bạn, và làm một vòng lặp dễ dàng từ 1 đến @@ ROWCOUNT của INSERT-SELECT.

Hãy thử điều này:

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

Điều này có vẻ giống như đơn giản nhất của lô. Cảm ơn! – Kuyenda

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 

Tin hay không, điều này thực sự hiệu quả và hiệu quả hơn sử dụng con trỏ.

+0

tại sao chọn tối đa mỗi lần trong vòng lặp? –

+0

Bạn có thể chọn nó một lần và lưu trữ nó trong một biến dễ dàng đủ ... đây chỉ là một vài thao tác gõ phím ngắn hơn. –

+1

tại sao chọn tối đa mỗi lần trong vòng lặp? Kết quả là, bạn phải nhấn biến bảng hai lần mỗi lần lặp. Bạn có thể loại bỏ SELECT MAX() trong WHILE() nếu bạn nắm bắt được @@ ROWCOUNT từ bảng varaible population, giống như tôi làm trong câu trả lời của tôi. –

6

Bạn có thể lặp qua biến bảng hoặc bạn có thể di chuột qua biến đó. Đây là những gì chúng ta thường gọi là một RBAR - phát âm lại và có nghĩa là Row-By-Agonizing-Row.

Tôi khuyên bạn nên tìm câu trả lời SET-BASED cho câu hỏi của bạn (chúng tôi có thể trợ giúp điều đó) và di chuyển ra khỏi rb càng nhiều càng tốt.

+0

Đây thực sự là lý do tại sao tôi muốn sử dụng biến bảng thay vì con trỏ. Tôi thường tìm cách để có được kết quả mong muốn của mình bằng cách sử dụng JOIN trên một biến bảng, nhưng nếu tôi không thể tìm cách sử dụng JOIN, thì tôi có thể quay trở lại trên một biến trên cùng một biến bảng đó. Nhưng tôi đồng ý, dựa trên thiết lập là tốt nhất. – Kuyenda

+0

Vòng lặp trên một biến bảng không tốt hơn con trỏ. Trong thực tế, nó thực sự có thể tồi tệ hơn. Lợi ích thực sự duy nhất của việc thay đổi mã từ con trỏ thành vòng lặp là "quyền khoe khoang". Ví dụ: "Tôi không có bất kỳ con trỏ nào trong mã của tôi". –

2

Đây là một câu trả lời khác, tương tự như Justin, nhưng không cần danh tính hoặc tổng hợp, chỉ là khóa chính (duy nhất).

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

mỗi lần lặp bạn nhấn vào biến bảng 3 lần, điều này không thể là hiệu quả –

2

Đây là biến thể của tôi. Khá giống như tất cả những người khác, nhưng tôi chỉ sử dụng một biến để quản lý vòng lặp.

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 

Raj Điểm khác có liên quan - chỉ thực hiện các vòng lặp nếu bạn cần.

2

Tôi không biết về cấu trúc WHILE.

Cấu trúc WHILE với biến bảng, tuy nhiên, trông giống như sử dụng CURSOR, trong đó bạn vẫn phải chọn hàng thành một biến dựa trên hàng IDENTITY, đây là một FETCH hiệu quả.

Có sự khác biệt nào giữa việc sử dụng WHERE và một số thông tin như sau không?

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

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

Tôi không biết điều đó có thể xảy ra hay không. Tôi cho rằng bạn có thể phải làm điều này:

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

Cảm ơn sự giúp đỡ của bạn!

+1

mã của bạn: _DECLARE cursor1 CURSOR CHO @ table1 OPEN cursor1_ sẽ không hoạt động. Các con trỏ phải có một SELECT trong định nghĩa của chúng, giống như ví dụ mã thứ hai của bạn. Nếu bạn thực hiện một số thử nghiệm, bạn sẽ thấy rằng vòng lặp mà không sử dụng con trỏ nhanh hơn vòng lặp bằng cách sử dụng con trỏ. –

1

Đây là phiên bản của tôi về cùng một giải pháp ...

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

nhìn như demo này:

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 mà không idRow, sử dụng 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

hai của tôi cent .. Từ câu trả lời của KM, nếu bạn muốn thả một. biến, bạn có thể thực hiện đếm ngược trên @RowsToProcess thay vì đếm ngược.

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

Đây là giải pháp tốt hơn vì câu trả lời được chấp nhận vì nó không phụ thuộc vào nội dung của biến bảng. – beerwin

0

Sau lần lưu thủ tục lặp qua biến bảng và in theo thứ tự tăng dần. Ví dụ này đang sử dụng 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; 

tuyên bố sau Thực thi các Stored Procedure:

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

Kết quả được hiển thị trong cửa sổ SQL Query được hiển thị dưới đây:

The Result of PrintSequenceSeries

Chức năng PARSE_COMMA_DELIMITED_INTEGER() trả về TABLE biến như sau:

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 
Các vấn đề liên quan