T-SQL:循环访问已知值的数组
这是我的场景:
比方说,我有一个存储过程,我需要在一组特定的ID上调用另一个存储过程; 有没有办法做到这一点?
即而不是需要这样做:
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
我的主要目标是简单的可维护性(易于删除/添加身份证的业务变化),能够列出所有的Id在一条线上…性能不应该是一个大问题
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
我在这种情况下做的是创build一个表variables来保存ID。
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
以上两种方法中的任何一种都比游标(对普通用户表声明)要快得多。 表值variables有一个不好的代表,因为如果使用不当,(对于行数很大的非常宽的表),它们不是高性能的。 但是如果你只用它们来保存一个键值或一个4字节的整数,并且有一个索引(就像在这个例子中那样),它们是非常快的。
使用静态游标variables和分割函数 :
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
游标有一个糟糕的代表,因为在针对用户表声明时的默认选项可能会产生大量开销。
但是在这种情况下,开销是非常小的,比其他任何方法都less。 STATIC告诉SQL Server在tempdb中实现结果,然后遍历它。 对于这样的小列表,这是最佳的解决scheme。
我通常使用以下方法
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
你可以尝试如下:
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