Voici mon scénario:
Disons que j'ai une procédure stockée dans laquelle j'ai besoin d'appeler une autre procédure stockée sur un ensemble d'identifiants spécifiques; Y a-t-il un moyen de faire cela?
c'est-à-dire au lieu d'avoir à le faire:
exec p_MyInnerProcedure 4
exec p_MyInnerProcedure 7
exec p_MyInnerProcedure 12
exec p_MyInnerProcedure 22
exec p_MyInnerProcedure 19
Faire quelque chose comme ça:
*magic where I specify my list contains 4,7,12,22,19*
DECLARE my_cursor CURSOR FAST_FORWARD FOR
*magic select*
OPEN my_cursor
FETCH NEXT FROM my_cursor INTO @MyId
WHILE @@FETCH_STATUS = 0
BEGIN
exec p_MyInnerProcedure @MyId
FETCH NEXT FROM my_cursor INTO @MyId
END
Mon objectif principal ici est simplement la maintenabilité (facile à supprimer / ajouter des identifiants au fur et à mesure que l'entreprise change), être capable de lister tous les identifiants sur une seule ligne ... Les performances ne devraient pas être un aussi gros problème
sql
sql-server
tsql
John
la source
la source
Réponses:
declare @ids table(idx int identity(1,1), id int) insert into @ids (id) select 4 union select 7 union select 12 union select 22 union select 19 declare @i int declare @cnt int select @i = min(idx) - 1, @cnt = max(idx) from @ids while @i < @cnt begin select @i = @i + 1 declare @id = select id from @ids where idx = @i exec p_MyInnerProcedure @id end
la source
Ce que je fais dans ce scénario, c'est créer une variable de table pour contenir les identifiants.
Declare @Ids Table (id integer primary Key not null) Insert @Ids(id) values (4),(7),(12),(22),(19)
- (ou appelez une autre fonction de table pour générer cette table)
Puis boucle en fonction des lignes de ce tableau
Declare @Id Integer While exists (Select * From @Ids) Begin Select @Id = Min(id) from @Ids exec p_MyInnerProcedure @Id Delete from @Ids Where id = @Id End
ou...
Declare @Id Integer = 0 -- assuming all Ids are > 0 While exists (Select * From @Ids where id > @Id) Begin Select @Id = Min(id) from @Ids Where id > @Id exec p_MyInnerProcedure @Id End
L'une ou l'autre des approches ci-dessus est beaucoup plus rapide qu'un curseur (déclaré par rapport aux tables utilisateur normales). Les variables table ont une mauvaise réputation car lorsqu'elles sont utilisées de manière incorrecte (pour des tables très larges avec un grand nombre de lignes) elles ne sont pas performantes. Mais si vous les utilisez uniquement pour contenir une valeur de clé ou un entier de 4 octets, avec un index (comme dans ce cas), ils sont extrêmement rapides.
la source
utilisez une variable de curseur statique et une fonction de fractionnement :
declare @comma_delimited_list varchar(4000) set @comma_delimited_list = '4,7,12,22,19' declare @cursor cursor set @cursor = cursor static for select convert(int, Value) as Id from dbo.Split(@comma_delimited_list) a declare @id int open @cursor while 1=1 begin fetch next from @cursor into @id if @@fetch_status <> 0 break ....do something.... end -- not strictly necessary w/ cursor variables since they will go out of scope like a normal var close @cursor deallocate @cursor
Les curseurs ont une mauvaise réputation car les options par défaut lorsqu'elles sont déclarées sur les tables utilisateur peuvent générer beaucoup de surcharge.
Mais dans ce cas, la surcharge est assez minime, inférieure à toutes les autres méthodes ici. STATIC indique à SQL Server de matérialiser les résultats dans tempdb, puis d'itérer dessus. Pour de petites listes comme celle-ci, c'est la solution optimale.
la source
Vous pouvez essayer comme ci-dessous:
declare @list varchar(MAX), @i int select @i=0, @list ='4,7,12,22,19,' while( @i < LEN(@list)) begin declare @item varchar(MAX) SELECT @item = SUBSTRING(@list, @i,CHARINDEX(',',@list,@i)-@i) select @item --do your stuff here with @item exec p_MyInnerProcedure @item set @i = CHARINDEX(',',@list,@i)+1 if(@i = 0) set @i = LEN(@list) end
la source
@list ='4,7,12,22,19' + ','
- il est donc tout à fait clair que la liste doit se terminer par une virgule (cela ne fonctionne pas sans elle!).J'utilise généralement l'approche suivante
DECLARE @calls TABLE ( id INT IDENTITY(1,1) ,parameter INT ) INSERT INTO @calls select parameter from some_table where some_condition -- here you populate your parameters declare @i int declare @n int declare @myId int select @i = min(id), @n = max(id) from @calls while @i <= @n begin select @myId = parameter from @calls where id = @i EXECUTE p_MyInnerProcedure @myId set @i = @i+1 end
la source
CREATE TABLE #ListOfIDs (IDValue INT) DECLARE @IDs VARCHAR(50), @ID VARCHAR(5) SET @IDs = @OriginalListOfIDs + ',' WHILE LEN(@IDs) > 1 BEGIN SET @ID = SUBSTRING(@IDs, 0, CHARINDEX(',', @IDs)); INSERT INTO #ListOfIDs (IDValue) VALUES(@ID); SET @IDs = REPLACE(',' + @IDs, ',' + @ID + ',', '') END SELECT * FROM #ListOfIDs
la source
Connectez-vous à votre base de données en utilisant un langage de programmation procédural (ici Python), et faites la boucle là-bas. De cette façon, vous pouvez également faire des boucles compliquées.
# make a connection to your db import pyodbc conn = pyodbc.connect(''' Driver={ODBC Driver 13 for SQL Server}; Server=serverName; Database=DBname; UID=userName; PWD=password; ''') cursor = conn.cursor() # run sql code for id in [4, 7, 12, 22, 19]: cursor.execute(''' exec p_MyInnerProcedure {} '''.format(id))
la source