98

I run a query select @id=table.id from table and I need to loop over the results so I can exec a store procedure for each row exec stored_proc @varName=@id,@otherVarName='test'

How can I do this in a T-SQL script?

3
  • well, I didn't try anything. I don't know how do do it. I suppose I could try to write a C style loop, but I doubt that will work. Maybe a PHP style loop, but again I doubt that will work too. I need to do it all in T-SQL in that I can run the SQL in SMS without an external language doing the looping... Hense the question in the first place. Commented Aug 7, 2012 at 19:35
  • 2
    @Shedal The irony of those comments/answers (though less than the "google it" answers) is that, over time they tend to become top google results. Thus in turn, when someone searches for a solution to the some problem, the first thing he will come across is a bashing (usually justified) of someone else who had the problem, but who didn't bother to search for the answer himself. In the long run, it becomes annoying to people who actually try to search for the answer. Commented May 28, 2014 at 10:14
  • @MarcinHabuszewski well, to be honest, in case there is no proper answer for a common question, it won't show up on the first page of Google search results. Normally. Commented May 28, 2014 at 13:38

8 Answers 8

209

You could use a CURSOR in this case:

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

Modified to show multiple parameters from the table.

Sign up to request clarification or add additional context in comments.

4 Comments

what if you want to get more than id from the table?
@HenleyChiu I have modified the answer to include a new parameter @name that I believe will work, but it has been a while since I have used a cursor now!.
@XN16 using more than id didn't work, work around has to be found :-(
See this answer for a method that uses only one FETCH statement. Also a couple of the other answers below use a single FETCH along with a "break" to exit the loop when the FETCH fails.
27

You could do something like this:

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 + 1)
          
        exec stored_proc @varName=@id, @otherVarName='test'
        
        set @currentrow = @currentrow +1
    end  

END

3 Comments

I like this method better since cursor are slower than while loop
Cursors are slower when compared to set operations (i.e. crafting one big select/update statement). When compared to creating a temporary table and doing some writes I am not so sure. See techrepublic.com/blog/the-enterprise-cloud/… I did my own tests and I concur. Possibly a temp table is faster when you have locking issues and you can get your subset faster and then process it. It depends I guess...
On SQL Server this gave me an error on the first iteration of the loop because row_number() starts from 1 but @currentrow is initialized to 0
10

My prefer solution is Microsoft KB 111401 http://support.microsoft.com/kb/111401.

The link refers to 3 examples:

This article describes various methods that you can use to simulate a cursor-like FETCH-NEXT logic in a stored procedure, trigger, or Transact-SQL batch.

/*********** 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

Comments

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 Comment

just add "OPEN @getid" before "WHILE 1=1"
0

try this:

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

Comments

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

Comments

0

I did not find more or less direct solution here, so I needed to use other resources and modify the code a little bit.

DECLARE @TrusteeElectionId INT;
    INSERT INTO dbo.TrusteeElections (CorpActId, ElectedOption )
    VALUES(@CorpActId,@ElectedOption)
    SET @trusteeElectionId = (select scope_identity())

    create table #ProductWrapperTypeIds
    (
        Id int
    )

     insert into #ProductWrapperTypeIds (id)
     SELECT value FROM STRING_SPLIT(@ProductWrapperTypes, ',') -- @ProductWrapperTypes is a story procedure parameter that looks like that '12,13,14' -- as a result of the code above #ProductWrapperTypeIds will have table with int IDs.


    declare @id int
    declare @totalrows int = (select count(*) from #ProductWrapperTypeIds)
    declare @currentId int = (SELECT MIN(Id) FROM  #ProductWrapperTypeIds) -- make sure we start from smaller id
    declare @currentrow int = (SELECT 0) -- counter starts from 0
    
    while @currentrow <  @totalrows  
   begin
    SET @id = NULL;
    SET @id = (select id from #ProductWrapperTypeIds where id = @currentId);
    IF @id IS NOT NULL
        BEGIN
              INSERT INTO dbo.TrusteeElectionProductTypes (ProductWrapperTypeId,TrusteeElectionId ) -- we don't want to insert NULL
              VALUES(@id, @TrusteeElectionId)
              SET @currentrow = @currentrow +1 -- we increase counter only if we find an id
        END
        SET @currentId = @currentId + 1 -- we promote id to the next item anyway


end

This example covers the conditions code too.

Comments

0

You can use the following approach:

SET @Id = (SELECT TOP 1 Id
                    FROM table)
 
WHILE @Id IS NOT NULL
BEGIN
    EXEC YourProcedure @Id
    
    SET @Id = (SELECT TOP 1 Id
                        FROM table)
    DELETE 
    FROM table
    WHERE Id = @Id
END

You may also wrap your stored procedure with a function, let's say function myFunc() and call it like this:

select myFunc(@id)
from table

1 Comment

Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.