it-swarm.com.de

Verwenden eines Cursors mit dynamischem SQL in einer gespeicherten Prozedur

Ich habe eine dynamische SQL-Anweisung, die ich in einer gespeicherten Prozedur erstellt habe. Ich muss die Ergebnisse mit einem Cursor durchlaufen. Es fällt mir schwer, die richtige Syntax zu finden. Hier ist was ich tue.

SELECT @SQLStatement = 'SELECT userId FROM users'

DECLARE @UserId

DECLARE users_cursor CURSOR FOR
EXECUTE @SQLStatment --Fails here. Doesn't like this

OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId

WHILE @@FETCH_STATUS = 0
BEGIN

EXEC asp_DoSomethingStoredProc @UserId

END
CLOSE users_cursor
DEALLOCATE users_cursor

Was ist der richtige Weg, um dies zu tun?

46
Micah

Ein Cursor akzeptiert nur eine select-Anweisung. Wenn die SQL also wirklich dynamisch sein muss, machen Sie den declare-Cursor zu einem Teil der Anweisung, die Sie ausführen. Damit das Folgende funktioniert, muss Ihr Server globale Cursor verwenden.

Declare @UserID varchar(100)
declare @sqlstatement nvarchar(4000)
--move declare cursor into sql to be executed
set @sqlstatement = 'Declare  users_cursor CURSOR FOR SELECT userId FROM users'

exec sp_executesql @sqlstatement


OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId

WHILE @@FETCH_STATUS = 0
BEGIN
Print @UserID
EXEC asp_DoSomethingStoredProc @UserId

FETCH NEXT FROM users_cursor --have to fetch again within loop
INTO @UserId

END
CLOSE users_cursor
DEALLOCATE users_cursor

Wenn Sie die Verwendung der globalen Cursor vermeiden müssen, können Sie die Ergebnisse Ihres dynamischen SQL auch in eine temporäre Tabelle einfügen und diese Tabelle dann zum Auffüllen des Cursors verwenden.

Declare @UserID varchar(100)
create table #users (UserID varchar(100))

declare @sqlstatement nvarchar(4000)
set @sqlstatement = 'Insert into #users (userID) SELECT userId FROM users'
exec(@sqlstatement)

declare users_cursor cursor for Select UserId from #Users
OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId

WHILE @@FETCH_STATUS = 0
BEGIN

EXEC asp_DoSomethingStoredProc @UserId

FETCH NEXT FROM users_cursor
INTO @UserId

END
CLOSE users_cursor
DEALLOCATE users_cursor

drop table #users
110
cmsjr

Dieser Code ist ein sehr gutes Beispiel für eine dynamische Spalte mit einem Cursor, da Sie in @STATEMENT kein '+' verwenden können:

ALTER PROCEDURE dbo.spTEST
AS
    SET NOCOUNT ON
    DECLARE @query NVARCHAR(4000) = N'' --DATA FILTER
    DECLARE @inputList NVARCHAR(4000) = ''
    DECLARE @field sysname = '' --COLUMN NAME
    DECLARE @my_cur CURSOR
    EXECUTE SP_EXECUTESQL
        N'SET @my_cur = CURSOR FAST_FORWARD FOR
            SELECT
                CASE @field
                    WHEN ''fn'' then fn
                    WHEN ''n_family_name'' then n_family_name
                END
            FROM
                dbo.vCard
            WHERE
                CASE @field
                    WHEN ''fn'' then fn
                    WHEN ''n_family_name'' then n_family_name
                END
                LIKE ''%''[email protected]+''%'';
            OPEN @my_cur;',
        N'@field sysname, @query NVARCHAR(4000), @my_cur CURSOR OUTPUT',
        @field = @field,
        @query = @query,
        @my_cur = @my_cur OUTPUT
    FETCH NEXT FROM @my_cur INTO @inputList
    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT @inputList
        FETCH NEXT FROM @my_cur INTO @inputList
    END
    RETURN
19
SMHMayboudi

Wenn Sie über eine ODBC) - Verbindung mit einer nicht relationalen Datenbank (IDMS?) Arbeiten, ist dies eine der Situationen, in denen Cursor und dynamisches SQL der einzige Weg zu sein scheinen.

select * from a where a=1 and b in (1,2)

es dauert 45 Minuten, um zu antworten, während das Neuschreiben zur Verwendung von Schlüsselsätzen ohne die in-Klausel in weniger als 1 Sekunde ausgeführt wird:

select * from a where (a=1 and b=1)
union all
select * from a where (a=1 and b=2)

Wenn die in-Anweisung für Spalte B 1145 Zeilen enthält, ist die Verwendung eines Cursors zum Erstellen einzelner Anweisungen und deren Ausführung als dynamisches SQL weitaus schneller als die Verwendung der in-Klausel. Blöd, hey?

Und ja, es gibt keine Zeit in einer relationalen Datenbank, in der der Cursor verwendet werden sollte. Ich kann einfach nicht glauben, dass ich auf einen Fall gestoßen bin, in dem eine Cursor-Schleife mehrere Größenordnungen schneller ist.

4
Twelfth

Vermeiden Sie zunächst die Verwendung eines Cursors, wenn dies möglich ist. Im Folgenden finden Sie einige Ressourcen, um das Problem zu beseitigen, wenn Sie offensichtlich nicht darauf verzichten können:

Es muss 15 Möglichkeiten geben, die Cursor zu verlieren ... Teil 1, Einführung

zeilenweise Verarbeitung ohne Cursor

Das heißt aber, Sie könnten doch an einem hängenbleiben - ich weiß nicht genug von Ihrer Frage, um sicherzugehen, dass beides zutrifft. In diesem Fall liegt ein anderes Problem vor: Die select-Anweisung für Ihren Cursor muss eine tatsächliche SELECT-Anweisung sein, keine EXECUTE-Anweisung. Du steckst fest.

Lesen Sie jedoch die Antwort von cmsjr (die beim Schreiben einging) über die Verwendung einer temporären Tabelle. Ich würde globale Cursor sogar mehr als "normale" vermeiden ....

3
RolandTumble

Nachdem ich kürzlich von Oracle zu SQL Server gewechselt bin (Arbeitgeberpräferenz), stelle ich fest, dass die Cursorunterstützung in SQL Server nachlässt. Cursor sind nicht immer böse, manchmal erforderlich, manchmal viel schneller und manchmal sauberer als der Versuch, eine komplexe Abfrage durch Neuanordnung oder Hinzufügen von Optimierungshinweisen zu optimieren. Die Meinung "Cursor sind böse" spielt in der SQL Server-Community eine viel größere Rolle.

Ich denke, diese Antwort ist, zu Oracle zu wechseln oder MS einen Hinweis zu geben.

1
crokusek

Es gibt ein weiteres Beispiel, das ich mit Ihnen teilen möchte
: D http://www.sommarskog.se/dynamic_sql.html#cursor

1
SMHMayboudi

Eine andere Option in SQL Server besteht darin, alle Ihre dynamischen Abfragen in Tabellenvariablen in einem gespeicherten Prozess durchzuführen und diese dann mit einem Cursor abzufragen und zu verarbeiten. In Bezug auf die gefürchtete Cursor-Debatte :) habe ich Studien gesehen, die zeigen, dass ein Cursor in manchen Situationen tatsächlich schneller sein kann, wenn er richtig eingerichtet ist. Ich benutze sie selbst, wenn die geforderte Abfrage zu komplex ist oder einfach nicht menschlich (für mich;)) möglich ist.

0
davaus