Question

J'ai créé une instruction SQL dynamique dans une procédure stockée. Je dois parcourir les résultats à l'aide d'un curseur. J'ai du mal à trouver la bonne syntaxe. Voici ce que je fais.

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

Quelle est la bonne façon de faire cela?

Était-ce utile?

La solution

Un curseur n'acceptera qu'une instruction select. Par conséquent, si le code SQL doit réellement être dynamique, placez le curseur de déclaration sur l'instruction que vous exécutez. Pour que le système ci-dessous fonctionne, votre serveur devra utiliser des curseurs globaux.

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

Si vous devez éviter d'utiliser les curseurs globaux, vous pouvez également insérer les résultats de votre code SQL dynamique dans une table temporaire, puis utiliser cette table pour renseigner votre curseur.

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

Autres conseils

Ce code est un très bon exemple de colonne dynamique avec un curseur, car vous ne pouvez pas utiliser '+' dans @STATEMENT:

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 ''%''+@query+''%'';
            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

Travailler avec une base de données non relationnelle (IDMS, quelqu'un?) sur une connexion ODBC est considéré comme l'un des cas où les curseurs et le code SQL dynamique semblent constituer l'unique voie.

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

prend 45 minutes pour répondre lorsqu'il est réécrit pour utiliser des jeux de clés sans la clause in s'exécutera dans moins d'une seconde:

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

Si l'instruction in de la colonne B contient 1145 lignes, l'utilisation d'un curseur pour créer des instructions individuelles et les exécuter en tant que code SQL dynamique est beaucoup plus rapide que l'utilisation de la clause in. Silly hey?

Et oui, il n'y a pas de temps dans une base de données relationnelle pour que le curseur soit utilisé. Je ne peux tout simplement pas croire que je suis tombé sur un cas où une boucle de curseur est plusieurs magnitudes plus rapide.

Tout d’abord, évitez autant que possible d’utiliser un curseur. Voici quelques ressources pour résoudre le problème quand il semble que vous ne pouvez pas vous en passer:

Il doit y avoir 15 façons de perdre vos curseurs ... partie 1, Introduction

Traitement ligne par ligne sans curseur

Cela dit, vous risquez peut-être de rester coincé l'un après l'autre - je ne connais pas suffisamment votre question pour être sûr que l'un ou l'autre de ces critères s'applique. Si tel est le cas, le problème est différent: l'instruction select de votre curseur doit être une instruction SELECT actuelle , et non une instruction EXECUTE. Vous êtes coincé.

Mais voyez la réponse de cmsjr (qui est arrivée pendant que j’écrivais) sur l’utilisation d’une table temporaire. J'éviterais les curseurs globaux encore plus que & "; Plain &"; ceux ....

Après avoir récemment basculé d'Oracle vers SQL Server (préférence de l'employeur), j'ai remarqué que la prise en charge du curseur dans SQL Server était en retard. Les curseurs ne sont pas toujours diaboliques, ils sont parfois nécessaires, parfois beaucoup plus rapides et parfois plus propres que d'essayer de régler une requête complexe en réorganisant ou en ajoutant des indicateurs d'optimisation. Les & "; Les curseurs sont diaboliques &"; l'opinion est beaucoup plus importante dans la communauté SQL Server.

Donc, je suppose que cette réponse consiste à passer à Oracle ou à donner un indice à MS.

Il y a un autre exemple que j'aimerais partager avec vous
: D http://www.sommarskog.se/dynamic_sql.html#cursor0

Une autre option de SQL Server consiste à effectuer toutes vos requêtes dynamiques dans une variable de table dans une procédure stockée, puis à utiliser un curseur pour interroger et traiter cette requête. En ce qui concerne le débat sur le curseur redouté :), j'ai vu des études montrant que dans certaines situations, un curseur peut être plus rapide s'il est correctement configuré. Je les utilise moi-même lorsque la requête requise est trop complexe, ou tout simplement pas humainement (pour moi;)) possible.

ce code peut vous être utile.

exemple d'utilisation du curseur sur le serveur SQL

DECLARE sampleCursor CURSOR FOR 
      SELECT K.Id FROM TableA K WHERE ....;
OPEN sampleCursor
FETCH NEXT FROM sampleCursor INTO @Id
WHILE @@FETCH_STATUS <> -1
BEGIN

UPDATE TableB
   SET 
      ...
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top