Есть ли способ перебрать табличную переменную в TSQL без использования курсора? - PullRequest
219 голосов
/ 15 сентября 2008

Допустим, у меня есть следующая простая табличная переменная:

declare @databases table
(
    DatabaseID    int,
    Name        varchar(15),   
    Server      varchar(15)
)
-- insert a bunch rows into @databases

Является ли объявление и использование курсора моим единственным вариантом, если я хочу перебирать строки? Есть ли другой способ?

Ответы [ 21 ]

2 голосов
/ 25 марта 2009

Я действительно не вижу смысла, почему вам нужно прибегать к использованию страшных cursor. Но вот еще один вариант, если вы используете SQL Server версии 2005/2008
Использование Рекурсия

declare @databases table
(
    DatabaseID    int,
    Name        varchar(15),   
    Server      varchar(15)
)

--; Insert records into @databases...

--; Recurse through @databases
;with DBs as (
    select * from @databases where DatabaseID = 1
    union all
    select A.* from @databases A 
        inner join DBs B on A.DatabaseID = B.DatabaseID + 1
)
select * from DBs
2 голосов
/ 25 января 2010

Я собираюсь предоставить решение на основе множеств.

insert  @databases (DatabaseID, Name, Server)
select DatabaseID, Name, Server 
From ... (Use whatever query you would have used in the loop or cursor)

Это намного быстрее, чем любой метод зацикливания, и его легче писать и поддерживать.

1 голос
/ 27 марта 2017

Вот мое решение, которое использует бесконечный цикл, оператор BREAK и функцию @@ROWCOUNT. Курсоры или временная таблица не нужны, и мне нужно написать только один запрос, чтобы получить следующую строку в таблице @databases:

declare @databases table
(
    DatabaseID    int,
    [Name]        varchar(15),   
    [Server]      varchar(15)
);


-- Populate the [@databases] table with test data.
insert into @databases (DatabaseID, [Name], [Server])
select X.DatabaseID, X.[Name], X.[Server]
from (values 
    (1, 'Roger', 'ServerA'),
    (5, 'Suzy', 'ServerB'),
    (8675309, 'Jenny', 'TommyTutone')
) X (DatabaseID, [Name], [Server])


-- Create an infinite loop & ensure that a break condition is reached in the loop code.
declare @databaseId int;

while (1=1)
begin
    -- Get the next database ID.
    select top(1) @databaseId = DatabaseId 
    from @databases 
    where DatabaseId > isnull(@databaseId, 0);

    -- If no rows were found by the preceding SQL query, you're done; exit the WHILE loop.
    if (@@ROWCOUNT = 0) break;

    -- Otherwise, do whatever you need to do with the current [@databases] table row here.
    print 'Processing @databaseId #' + cast(@databaseId as varchar(50));
end
1 голос
/ 13 марта 2017

Для этого можно использовать курсор:

создать функцию [dbo] .f_teste_loop возвращает таблицу @tabela ( треска, ном варчар (10) ) как начать

insert into @tabela values (1, 'verde');
insert into @tabela values (2, 'amarelo');
insert into @tabela values (3, 'azul');
insert into @tabela values (4, 'branco');

return;

конец

процедура создания [dbo]. [Sp_teste_loop] как начать

DECLARE @cod int, @nome varchar(10);

DECLARE curLoop CURSOR STATIC LOCAL 
FOR
SELECT  
    cod
   ,nome
FROM 
    dbo.f_teste_loop();

OPEN curLoop;

FETCH NEXT FROM curLoop
           INTO @cod, @nome;

WHILE (@@FETCH_STATUS = 0)
BEGIN
    PRINT @nome;

    FETCH NEXT FROM curLoop
           INTO @cod, @nome;
END

CLOSE curLoop;
DEALLOCATE curLoop;

конец

1 голос
/ 18 октября 2016

Я предпочитаю использовать Offset Fetch, если у вас есть уникальный идентификатор, вы можете отсортировать таблицу по:

DECLARE @TableVariable (ID int, Name varchar(50));
DECLARE @RecordCount int;
SELECT @RecordCount = COUNT(*) FROM @TableVariable;

WHILE @RecordCount > 0
BEGIN
SELECT ID, Name FROM @TableVariable ORDER BY ID OFFSET @RecordCount - 1 FETCH NEXT 1 ROW;
SET @RecordCount = @RecordCount - 1;
END

Таким образом, мне не нужно добавлять поля в таблицу или использовать оконную функцию.

1 голос
/ 16 июня 2016

Этот подход требует только одну переменную и не удаляет строки из @databases. Я знаю, что здесь есть много ответов, но я не вижу такого, который бы использовал MIN для получения следующего идентификатора.

DECLARE @databases TABLE
(
    DatabaseID    int,
    Name        varchar(15),   
    Server      varchar(15)
)

-- insert a bunch rows into @databases

DECLARE @CurrID INT

SELECT @CurrID = MIN(DatabaseID)
FROM @databases

WHILE @CurrID IS NOT NULL
BEGIN

    -- Do stuff for @CurrID

    SELECT @CurrID = MIN(DatabaseID)
    FROM @databases
    WHERE DatabaseID > @CurrID

END
1 голос
/ 02 мая 2014

Это будет работать в версии SQL SERVER 2012.

declare @Rowcount int 
select @Rowcount=count(*) from AddressTable;

while( @Rowcount>0)
  begin 
 select @Rowcount=@Rowcount-1;
 SELECT * FROM AddressTable order by AddressId desc OFFSET @Rowcount ROWS FETCH NEXT 1 ROWS ONLY;
end 
1 голос
/ 15 сентября 2008

Я согласен с предыдущим постом, что основанные на множестве операции обычно будут работать лучше, но если вам нужно перебирать строки, вот подход, который я бы выбрал:

  1. Добавить новое поле в табличную переменную (тип данных бит, по умолчанию 0)
  2. Вставьте свои данные
  3. Выберите строку Top 1, где fUsed = 0 (Примечание: fUsed - это имя поля в шаге 1)
  4. Выполните любую необходимую обработку
  5. Обновите запись в вашей табличной переменной, установив для записи значение fUsed = 1
  6. Выберите следующую неиспользуемую запись из таблицы и повторите процесс

    DECLARE @databases TABLE  
    (  
        DatabaseID  int,  
        Name        varchar(15),     
        Server      varchar(15),   
        fUsed       BIT DEFAULT 0  
    ) 
    
    -- insert a bunch rows into @databases
    
    DECLARE @DBID INT
    
    SELECT TOP 1 @DBID = DatabaseID from @databases where fUsed = 0 
    
    WHILE @@ROWCOUNT <> 0 and @DBID IS NOT NULL  
    BEGIN  
        -- Perform your processing here  
    
        --Update the record to "used" 
    
        UPDATE @databases SET fUsed = 1 WHERE DatabaseID = @DBID  
    
        --Get the next record  
        SELECT TOP 1 @DBID = DatabaseID from @databases where fUsed = 0   
    END
    
0 голосов
/ 02 сентября 2014

Шаг 1: Оператор select ниже создает временную таблицу с уникальным номером строки для каждой записи.

select eno,ename,eaddress,mobno int,row_number() over(order by eno desc) as rno into #tmp_sri from emp 

Step2: объявить обязательные переменные

DECLARE @ROWNUMBER INT
DECLARE @ename varchar(100)

Шаг 3: взять общее количество строк из временной таблицы

SELECT @ROWNUMBER = COUNT(*) FROM #tmp_sri
declare @rno int

Step4: создание таблицы временных циклов на основе уникального номера строки, создаваемого в temp

while @rownumber>0
begin
  set @rno=@rownumber
  select @ename=ename from #tmp_sri where rno=@rno  **// You can take columns data from here as many as you want**
  set @rownumber=@rownumber-1
  print @ename **// instead of printing, you can write insert, update, delete statements**
end
0 голосов
/ 20 мая 2014

лучше выбрать @pk = @pk + 1: SET @pk + = @pk. Избегайте использования SELECT, если вы не ссылаетесь на таблицы, которые просто присваивают значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...