T-SQL-Schleife über Abfrageergebnisse


76

Ich führe eine Abfrage aus select @id=table.id from tableund muss die Ergebnisse durchlaufen, damit ich für jede Zeile eine Speicherprozedur ausführen kannexec stored_proc @varName=@id,@otherVarName='test'

Wie kann ich das in einem T-SQL-Skript tun?


Nun, ich habe nichts versucht. Ich weiß nicht, wie es geht. Ich denke, ich könnte versuchen, eine Stilschleife zu schreiben C, aber ich bezweifle, dass das funktionieren wird. Vielleicht eine PHPStyle-Schleife, aber ich bezweifle, dass das auch funktionieren wird. Ich muss alles in T-SQL tun, damit ich SQL in SMS ausführen kann, ohne dass eine externe Sprache die Schleife ausführt ... Hense die Frage an erster Stelle.
Justin808

2
@Shedal Die Ironie dieser Kommentare / Antworten (obwohl weniger als die "google it" -Antworten) ist, dass sie im Laufe der Zeit zu Top-Google-Ergebnissen werden. Wenn also jemand nach einer Lösung für ein Problem sucht, wird er als erstes auf einen (normalerweise gerechtfertigten) Schlag gegen jemanden stoßen, der das Problem hatte, sich aber nicht die Mühe gemacht hat, selbst nach der Antwort zu suchen. Auf lange Sicht wird es für Leute ärgerlich, die tatsächlich versuchen, nach der Antwort zu suchen.
Jahu

@MarcinHabuszewski Nun, um ehrlich zu sein, falls es keine richtige Antwort auf eine häufig gestellte Frage gibt, wird sie nicht auf der ersten Seite der Google-Suchergebnisse angezeigt. Normalerweise.
Dmytro Shevchenko

Antworten:


172

In diesem Fall können Sie einen CURSOR verwenden:

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

Geändert, um mehrere Parameter aus der Tabelle anzuzeigen.


1
Was ist, wenn Sie mehr als nur ID von der Tabelle erhalten möchten?
Henley Chiu

2
@HenleyChiu Ich habe die Antwort geändert, um einen neuen Parameter aufzunehmen @name, von dem ich glaube, dass er funktionieren wird, aber es ist schon eine Weile her, seit ich jetzt einen Cursor verwendet habe!.
XN16

@ XN16 mit mehr als ID hat nicht funktioniert, Workaround muss gefunden werden :-(
Zaveed Abbasi

22

Sie könnten so etwas tun:

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

Ich mag diese Methode besser, da der Cursor langsamer ist als die while-Schleife
PL Audet

3
Cursor sind langsamer als festgelegte Operationen (dh das Erstellen einer großen Auswahl- / Aktualisierungsanweisung). Im Vergleich zum Erstellen einer temporären Tabelle und zum Schreiben bin ich mir nicht so sicher. Siehe techrepublic.com/blog/the-enterprise-cloud/… Ich habe meine eigenen Tests durchgeführt und bin damit einverstanden. Möglicherweise ist eine temporäre Tabelle schneller, wenn Sie Sperrprobleme haben, und Sie können Ihre Teilmenge schneller abrufen und dann verarbeiten. Es kommt darauf an, denke ich ...
phil_w

Unter SQL Server gab es bei der ersten Iteration der Schleife einen Fehler, da row_number () bei 1 beginnt, @currentrow jedoch auf 0 initialisiert wird
samgak

8

Meine bevorzugte Lösung ist Microsoft KB 111401 http://support.microsoft.com/kb/111401 .

Der Link bezieht sich auf 3 Beispiele:

In diesem Artikel werden verschiedene Methoden beschrieben, mit denen Sie eine cursorähnliche FETCH-NEXT-Logik in einer gespeicherten Prozedur, einem Trigger oder einem Transact-SQL-Stapel simulieren können.

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

Fügen Sie einfach "OPEN @getid" vor "WHILE 1 = 1" hinzu
Ian

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

Versuche dies:

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.