Aqui está o meu cenário:
Digamos que eu tenha um procedimento armazenado no qual preciso chamar outro procedimento armazenado em um conjunto de IDs específicos; existe uma maneira de fazer isso?
ou seja, em vez de precisar fazer isso:
exec p_MyInnerProcedure 4
exec p_MyInnerProcedure 7
exec p_MyInnerProcedure 12
exec p_MyInnerProcedure 22
exec p_MyInnerProcedure 19
Fazendo algo assim:
*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
Meu objetivo principal aqui é simplesmente manutenção (fácil de remover / adicionar ids conforme o negócio muda), ser capaz de listar todos os ids em uma única linha ... O desempenho não deve ser um problema tão grande
sql
sql-server
tsql
John
fonte
fonte
Respostas:
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
fonte
O que faço neste cenário é criar uma variável de tabela para conter os Ids.
Declare @Ids Table (id integer primary Key not null) Insert @Ids(id) values (4),(7),(12),(22),(19)
- (ou chame outra função com valor de tabela para gerar esta tabela)
Em seguida, faça um loop com base nas linhas desta tabela
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
Qualquer uma das abordagens acima é muito mais rápida do que um cursor (declarado em relação à (s) Tabela (s) de usuário normal). Variáveis com valor de tabela têm uma má reputação porque, quando usadas incorretamente (para tabelas muito largas com grande número de linhas), elas não apresentam desempenho. Mas se você os estiver usando apenas para manter um valor-chave ou um inteiro de 4 bytes, com um índice (como neste caso) eles são extremamente rápidos.
fonte
use uma variável de cursor estática e uma função de divisão :
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
Os cursores têm má reputação, pois as opções padrão, quando declaradas nas tabelas do usuário, podem gerar muita sobrecarga.
Mas, neste caso, a sobrecarga é mínima, menor do que qualquer outro método aqui. STATIC diz ao SQL Server para materializar os resultados em tempdb e, em seguida, iterar sobre isso. Para pequenas listas como essa, é a solução ideal.
fonte
Você pode tentar o seguinte:
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
fonte
@list ='4,7,12,22,19' + ','
- então é totalmente claro que a lista deve terminar com uma vírgula (ela não funciona sem ela!).Eu costumo usar a seguinte abordagem
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
fonte
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
fonte
Faça uma conexão com seu banco de dados usando uma linguagem de programação procedural (aqui Python) e faça o loop ali. Dessa forma, você também pode fazer loops 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))
fonte