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?
Un curseur accepte uniquement une instruction select. Par conséquent, si le code SQL doit être dynamique, intégrez le curseur de déclaration à 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 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
Ce code est un très bon exemple pour une 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 l’un des cas où les curseurs et le code SQL dynamique semblent constituer le seul chemin.
select * from a where a=1 and b in (1,2)
cela prend 45 minutes pour répondre en réécrivant pour utiliser des jeux de clés sans que la clause in s'exécute en 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 où le curseur doit être utilisé. Je ne peux tout simplement pas croire que je sois tombé sur un cas où une boucle de curseur est plusieurs magnitudes plus rapide.
Tout d'abord, évitez d'utiliser un curseur si possible. 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 vous en prendre une après tout - je ne connais pas suffisamment votre question pour être sûre que l'une ou l'autre de ces conditions s'applique. Si tel est le cas, vous avez un problème différent: l'instruction select de votre curseur doit être une instruction SELECT réelle et non une instruction EXECUTE. Tu es coincé.
Mais voyez la réponse de cmsjr (qui est arrivée pendant que j'écrivais) sur l'utilisation d'une table temporaire. Je voudrais éviter les curseurs globaux encore plus que les curseurs "simples" ....
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 "curseurs sont mauvais" l'opinion est beaucoup plus importante dans la communauté SQL Server.
Je suppose donc que cette réponse consiste à passer à Oracle ou à donner un indice à MS.
for
implicitement définit/ouvre/ferme le curseur!)Il y a un autre exemple que j'aimerais partager avec vous
: D http://www.sommarskog.se/dynamic_sql.html#cursor
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 dernière. En ce qui concerne le débat sur le curseur redouté :), j'ai vu des études qui montrent 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.