Aquí está mi escenario:
Digamos que tengo un procedimiento almacenado en el que necesito llamar a otro procedimiento almacenado en un conjunto de identificadores específicos; ¿Hay alguna forma de hacer esto?
es decir, en lugar de tener que hacer esto:
exec p_MyInnerProcedure 4
exec p_MyInnerProcedure 7
exec p_MyInnerProcedure 12
exec p_MyInnerProcedure 22
exec p_MyInnerProcedure 19
Haciendo algo como esto:
*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
Mi objetivo principal aquí es simplemente la capacidad de mantenimiento (fácil de eliminar / agregar identificaciones a medida que cambia el negocio), poder enumerar todas las identificaciones en una sola línea ... El rendimiento no debería ser un problema tan grande
sql
sql-server
tsql
Juan
fuente
fuente
Respuestas:
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
fuente
Lo que hago en este escenario es crear una variable de tabla para contener los ID.
Declare @Ids Table (id integer primary Key not null) Insert @Ids(id) values (4),(7),(12),(22),(19)
- (o llame a otra función con valor de tabla para generar esta tabla)
Luego, haz un bucle basado en las filas de esta tabla.
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
o...
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
Cualquiera de los enfoques anteriores es mucho más rápido que un cursor (declarado contra las tablas de usuario normales). Las variables con valores de tabla tienen una mala reputación porque cuando se usan incorrectamente (para tablas muy amplias con un gran número de filas) no funcionan. Pero si los está usando solo para contener un valor clave o un entero de 4 bytes, con un índice (como en este caso) son extremadamente rápidos.
fuente
use una variable de cursor estática y una función de división :
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
Los cursores tienen una mala reputación, ya que las opciones predeterminadas cuando se declaran en las tablas de usuario pueden generar una gran sobrecarga.
Pero en este caso la sobrecarga es mínima, menor que cualquier otro método aquí. STATIC le dice a SQL Server que materialice los resultados en tempdb y luego repita sobre eso. Para listas pequeñas como esta, es la solución óptima.
fuente
Puede intentar lo siguiente:
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
fuente
@list ='4,7,12,22,19' + ','
- así que está totalmente claro que la lista tiene que terminar con una coma (¡no funciona sin ella!).Yo suelo usar el siguiente enfoque
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
fuente
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
fuente
Haga una conexión a su base de datos usando un lenguaje de programación procedimental (aquí Python) y haga el ciclo allí. De esta manera, también puede hacer bucles complicados.
# 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))
fuente