Цикл T-SQL над результатами запиту


78

Я запускаю запит, select @id=table.id from tableі мені потрібно прокрутити результати, щоб я міг виконати процедуру зберігання для кожного рядкаexec stored_proc @varName=@id,@otherVarName='test'

Як я можу це зробити у сценарії T-SQL?


ну, я нічого не пробував. Я не знаю, як це зробити. Думаю, я міг би спробувати написати Cцикл стилів, але я сумніваюся, що це спрацює. Можливо, PHPцикл стилів, але я знову сумніваюся, що це теж спрацює. Мені потрібно зробити все це в T-SQL, щоб я міг запускати SQL в SMS без зовнішньої мови, що робить цикл ... Спершу поставте питання.
Justin808,

2
@Shedal Іронія цих коментарів / відповідей (хоча менше, ніж відповіді "google it") полягає в тому, що з часом вони, як правило, стають найкращими результатами google. Таким чином, у свою чергу, коли хтось шукає рішення якоїсь проблеми, перше, на що він натрапляє, це побиття (як правило, виправдане) когось іншого, хто мав проблему, але хто не потрудився шукати відповідь сам. Зрештою це стає прикро для людей, які насправді намагаються знайти відповідь.
джаху

@MarcinHabuszewski ну, чесно кажучи, якщо немає належної відповіді на поширене запитання, воно не з’явиться на першій сторінці результатів пошуку Google. Зазвичай.
Дмитро Шевченко

Відповіді:


174

У цьому випадку ви можете використовувати КУРСОР:

DECLARE @id INT
DECLARE @name NVARCHAR(100)
DECLARE @getid CURSOR

SET @getid = CURSOR FOR
SELECT table.id,
       table.name
FROM   table

OPEN @getid
FETCH NEXT
FROM @getid INTO @id, @name
WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC stored_proc @varName=@id, @otherVarName='test', @varForName=@name
    FETCH NEXT
    FROM @getid INTO @id, @name
END

CLOSE @getid
DEALLOCATE @getid

Змінено для відображення декількох параметрів з таблиці.


1
що робити, якщо ви хочете отримати більше, ніж ідентифікатор зі столу?
Henley Chiu

2
@HenleyChiu Я змінив відповідь, включивши новий параметр, @nameякий, на мою думку, спрацює, але минув деякий час, відколи я використовую курсор !.
XN16,

@ XN16 з використанням більш ніж ідентифікатора не спрацював, треба
обійтись

22

Ви можете зробити щось подібне:

create procedure test
as
BEGIN

    create table #ids
    (
        rn int,
        id int
    )

    insert into #ids (rn, id)
    select distinct row_number() over(order by id) as rn, id
    from table

    declare @id int
    declare @totalrows int = (select count(*) from #ids)
    declare @currentrow int = 0

    while @currentrow <  @totalrows  
    begin 
        set @id = (select id from #ids where rn = @currentrow)

        exec stored_proc @varName=@id, @otherVarName='test'

        set @currentrow = @currentrow +1
    end  

END

Цей метод мені більше подобається, оскільки курсор повільніший, ніж цикл while
PL Audet

3
Курсори повільніші порівняно із заданими операціями (тобто створення одного великого оператора вибору / оновлення). У порівнянні зі створенням тимчасової таблиці та виконанням деяких записів я не настільки впевнений. Дивіться techrepublic.com/blog/the-enterprise-cloud/... Я власноруч тестував і погоджуюсь. Можливо, тимчасова таблиця швидша, коли виникають проблеми із блокуванням, і ви можете швидше отримати підмножину, а потім обробити її. Це залежить, я здогадуюсь ...
phil_w

На SQL Server це призвело до помилки на першій ітерації циклу, оскільки row_number () починається з 1, але @currentrow ініціалізується до 0
samgak

8

Мені більше подобається рішення - Microsoft KB 111401 http://support.microsoft.com/kb/111401 .

Посилання посилається на 3 приклади:

У цій статті описано різні методи, які можна використовувати для імітації логіки FETCH-NEXT, схожої на курсор, у збереженій процедурі, тригері або пакетній програмі Transact-SQL.

/*********** example 1 ***********/ 

declare @au_id char( 11 )

set rowcount 0
select * into #mytemp from authors

set rowcount 1

select @au_id = au_id from #mytemp

while @@rowcount <> 0
begin
    set rowcount 0
    select * from #mytemp where au_id = @au_id
    delete #mytemp where au_id = @au_id

    set rowcount 1
    select @au_id = au_id from #mytemp
end
set rowcount 0



/********** example 2 **********/ 

declare @au_id char( 11 )

select @au_id = min( au_id ) from authors

while @au_id is not null
begin
    select * from authors where au_id = @au_id
    select @au_id = min( au_id ) from authors where au_id > @au_id
end



/********** example 3 **********/ 

set rowcount 0
select NULL mykey, * into #mytemp from authors

set rowcount 1
update #mytemp set mykey = 1

while @@rowcount > 0
begin
    set rowcount 0
    select * from #mytemp where mykey = 1
    delete #mytemp where mykey = 1
    set rowcount 1
    update #mytemp set mykey = 1
end
set rowcount 0

3
DECLARE @id INT
DECLARE @name NVARCHAR(100)
DECLARE @getid CURSOR

SET @getid = CURSOR FOR
SELECT table.id,
       table.name
FROM   table

WHILE 1=1
BEGIN

    FETCH NEXT
    FROM @getid INTO @id, @name
    IF @@FETCH_STATUS < 0 BREAK

    EXEC stored_proc @varName=@id, @otherVarName='test', @varForName=@name

END

CLOSE @getid
DEALLOCATE @getid

1
просто додайте "OPEN @getid" перед "WHILE 1 = 1"
Ян

0
DECLARE @id INT
DECLARE @filename NVARCHAR(100)
DECLARE @getid CURSOR

SET @getid = CURSOR FOR
SELECT top 3 id,
filename 
FROM  table

OPEN @getid
WHILE 1=1
BEGIN

    FETCH NEXT
    FROM @getid INTO @id, @filename
    IF @@FETCH_STATUS < 0 BREAK

    print @id

END


CLOSE @getid
DEALLOCATE @getid

-1

спробуйте це:

declare @i tinyint = 0,
    @count tinyint,
    @id int,
    @name varchar(max)

select @count = count(*) from table
while (@i < @count)
begin
    select @id = id, @name = name from table
    order by nr asc offset @i rows fetch next 1 rows only

    exec stored_proc @varName = @id, @otherVarName = 'test', @varForName = @name

    set @i = @i + 1
end
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.