Вот мой сценарий:
Допустим, у меня есть хранимая процедура, в которой мне нужно вызвать другую хранимую процедуру для набора определенных идентификаторов; Есть ли способ сделать это?
т.е. вместо этого:
exec p_MyInnerProcedure 4
exec p_MyInnerProcedure 7
exec p_MyInnerProcedure 12
exec p_MyInnerProcedure 22
exec p_MyInnerProcedure 19
Делаем что-то вроде этого:
*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
Моя главная цель здесь - простая ремонтопригодность (легко удалить / добавить идентификаторы по мере изменения бизнеса), возможность перечислить все идентификаторы в одной строке ... Производительность не должна быть такой большой проблемой
sql
sql-server
tsql
Джон
источник
источник
Ответы:
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
источник
Что я делаю в этом сценарии, так это создаю табличную переменную для хранения идентификаторов.
Declare @Ids Table (id integer primary Key not null) Insert @Ids(id) values (4),(7),(12),(22),(19)
- (или вызовите другую функцию с табличным значением для создания этой таблицы)
Затем выполните цикл на основе строк в этой таблице
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
или же...
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
Любой из вышеперечисленных подходов намного быстрее, чем курсор (объявленный для обычных пользовательских таблиц). Переменные с табличным значением имеют плохую репутацию, потому что при неправильном использовании (для очень широких таблиц с большим количеством строк) они неэффективны. Но если вы используете их только для хранения ключевого значения или 4-байтового целого числа с индексом (как в этом случае), они работают очень быстро.
источник
используйте статическую курсорную переменную и функцию разделения :
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
Курсоры имеют плохую репутацию, поскольку параметры по умолчанию, объявленные для пользовательских таблиц, могут вызвать много накладных расходов.
Но в этом случае накладные расходы минимальны, меньше, чем любые другие методы здесь. STATIC сообщает SQL Server материализовать результаты в tempdb, а затем перебирать их. Для таких небольших списков это оптимальное решение.
источник
Вы можете попробовать, как показано ниже:
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
источник
@list ='4,7,12,22,19' + ','
- чтобы было совершенно ясно, что список должен заканчиваться запятой (без нее он не работает!).Обычно я использую следующий подход
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
источник
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
источник
Подключитесь к своей БД с помощью процедурного языка программирования (здесь Python) и выполните цикл там. Таким образом, вы также можете делать сложные петли.
# 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))
источник