Je cherche un moyen d'appeler une procédure stockée pour chaque enregistrement d'une instruction select.
SELECT @SomeIds = (
SELECT spro.Id
FROM SomeTable as spro
INNER JOIN [Address] addr ON addr.Id = spro.Id
INNER JOIN City cty ON cty.CityId = addr.CityId
WHERE cty.CityId = @CityId
)
WHILE @SomeIds IS NOT NULL
BEGIN
EXEC UpdateComputedFullText @SomeIds
END
Une telle chose ne fonctionne pas, bien sûr, mais existe-t-il un moyen de faire quelque chose comme ça?
Vous devez utiliser un curseur pour cela.
DECLARE @oneid int -- or the appropriate type
DECLARE the_cursor CURSOR FAST_FORWARD
FOR SELECT spro.Id
FROM SomeTable as spro
INNER JOIN [Address] addr ON addr.Id = spro.Id
INNER JOIN City cty ON cty.CityId = addr.CityId
WHERE cty.CityId = @CityId
OPEN the_cursor
FETCH NEXT FROM the_cursor INTO @oneid
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC UpdateComputedFullText @oneid
FETCH NEXT FROM the_cursor INTO @oneid
END
CLOSE the_cursor
DEALLOCATE the_cursor
Surpris, personne ne vous a donné une réponse à jour. Les curseurs sont mauvais. Ce que vous voulez, c'est déplacer la logique du SP dans une fonction TVF ( table-values-function) puis utiliser CROSS APPLY
Voici une requête que j'ai écrite hier (ne vous attardez pas sur les détails, regardez simplement le CROSS APPLY
). Le CROSS APPLY
crée une union de tables. Chaque élément de cette union est généré à partir de la TVF qui est paramétrée dans les entrées de ligne de l'instruction select.
SELECT supt.hostname,supt.scriptname, COUNT(*)
FROM Event_Pagehit eph
INNER JOIN Symboltable_urlpair supf
ON eph.fromPagePair=supf.id
INNER JOIN Symboltable_urlpair supt
ON supt.id=eph.toPagePair
CROSS APPLY dbo.TDFCompanyFormationsUrlClassification(supf.hostname,supf.scriptname) as x
CROSS APPLY dbo.TDFCompanyFormationsUrlClassification(supt.hostname,supt.scriptname) as y
WHERE x.isCompanyFormations=1
AND y.isCompanyFormations=0
GROUP BY supt.hostname,supt.scriptname
ORDER BY COUNT(*) desc
Je peux utiliser x
et y
comme s’il s’agissait de tables extraites des clauses FROM
ou JOIN
. Si je devais écrire cette requête sans TVF, cela couvrirait quelques centaines de lignes.
Remarque:
Si vous ne pouvez pas réécrire le SP, vous devriez pouvoir insérer le résultat d'une procédure stockée dans la table de résultats à partir d'une fonction table. Je n'ai jamais fait cela, et parfois la construction différente du serveur SQL a des mises en garde - Donc, à moins que quelqu'un ne dit le contraire, je suppose que c'est le cas.
Placez les ID dans une variable de table temporaire, puis parcourez chaque ligne: (vous n'avez pas besoin d'utiliser un curseur qui sera beaucoup plus lent)
Declare @Keys Table (key integer Primary Key Not Null)
Insert @Keys(key)
SELECT spro.Id
FROM SomeTable as spro
JOIN [Address] addr ON addr.Id = spro.Id
JOIN City cty ON cty.CityId = addr.CityId
WHERE cty.CityId = @CityId
-- -------------------------------------------
Declare @Key Integer
While Exists (Select * From @Keys)
Begin
Select @Key = Max(Key) From @Keys
EXEC UpdateComputedFullText @Key
Delete @Keys Where Key = @Key
End
EDIT Delete n'est pas lent lorsqu'il est utilisé avec un prédicat de filtre piloté par un index unique très étroit, comme c'est le cas. Mais cela peut facilement être évité, simplement en faisant la boucle comme suit:
Declare @Key Integer = 0
While Exists (Select * From @Keys
Where key > @Key)
Begin
Select @Key = Min(Key) From @Keys
Where key > @Key
EXEC UpdateComputedFullText @Key
-- Delete @Keys Where Key = @Key No Longer necessary
End
Essayez celui-ci sans curseur
DECLARE @id int
SELECT top 1 @id = spro.Id
FROM SomeTable as spro
INNER JOIN [Address] addr ON addr.Id = spro.Id
INNER JOIN City cty ON cty.CityId = addr.CityId
WHERE cty.CityId = @CityId
ORDER BY spro.id
WHILE @@ROWCOUNT > 0
BEGIN
EXEC UpdateComputedFullText @id
SELECT top 1 @id = spro.Id
FROM SomeTable as spro
INNER JOIN [Address] addr ON addr.Id = spro.Id
INNER JOIN City cty ON cty.CityId = addr.CityId
WHERE cty.CityId = @CityId
and spro.id > @id
ORDER BY spro.id
END
Les deux réponses ci-dessus les curseurs RE sont correctes. Toutefois, en fonction de la complexité du code exécuté à l'intérieur du curseur, il serait peut-être préférable de le déposer dans la langue de votre choix et d'effectuer vos calculs en code avant de transférer les résultats dans une base de données.
Je me suis retrouvé à revenir en arrière et à passer en revue de nombreuses opérations de curseur et, dans de nombreux cas, à les convertir en code pour des raisons de performances.
Vous devrez utiliser un curseur: Exemples de curseurs SQL Server
DECLARE @id int
DECLARE cursor_sample CURSOR FOR
SELECT spro.Id
FROM SomeTable as spro
INNER JOIN [Address] addr ON addr.Id = spro.Id
INNER JOIN City cty ON cty.CityId = addr.CityId
WHERE cty.CityId = @CityId
OPEN cursor_sample
FETCH NEXT FROM cursor_sample INTO @id
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC UpdateComputedFullText @id
FETCH NEXT FROM cursor_sample INTO @id
END
CLOSE cursor_sample
DEALLOCATE cursor_sample
Avez-vous vraiment besoin d'effectuer un traitement ligne par ligne lorsque le traitement des ensembles est disponible?
Vous pouvez placer les résultats de SELECT dans une table temporaire, puis appeler un proc pour exécuter du SQL en bloc sur le contenu de la table temporaire. La table temporaire sera disponible pour le processus appelé en fonction des règles de portée T-SQL.
La solution de curseur standard est un mal pour un mal ... Deux déclarations identiques de FETCH NEXT ne sont qu'un cauchemar de maintenance.
mieux c'est
...declare cursor etc.
While 1=1
Fetch ...
if @@FETCH_STATUS <> 0 BREAK
...
End -- While
..Close cursor etc.
Un mal parfois justifié. Essayez simplement de concevoir une approche basée sur un ensemble pour envoyer des courriels de notification en utilisant sp_send_dbmail ou une autre procédure stockée.