T-SQL:在已知值数组中循环

T-SQL:在已知值数组中循环,sql,sql-server,tsql,Sql,Sql Server,Tsql,以下是我的设想: 假设我有一个存储过程,其中我需要调用一组特定ID上的另一个存储过程;有办法做到这一点吗 i、 e.不需要这样做: 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,1

以下是我的设想:

假设我有一个存储过程,其中我需要调用一组特定ID上的另一个存储过程;有办法做到这一点吗

i、 e.不需要这样做:

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,能够在一行中列出所有id。。。性能不应该是一个大问题

在这个场景中,我要做的是创建一个表变量来保存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
  While exists (Select * From @Ids)
    Begin
      Select @Id = Min(id) from @Ids
      exec p_MyInnerProcedure @Id 
      Delete from @Ids Where id = @Id
    End
或者


上述任何一种方法都比针对常规用户表声明的游标快得多。表值变量的代表性不好,因为当使用不当时,对于包含大量行的非常宽的表,它们无法执行。但是,如果您仅使用它们来保存键值或4字节整数,并且像本例中那样使用索引,则它们的速度非常快

使用静态游标变量和:

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
游标的代表性不好,因为针对用户表声明默认选项时会产生大量开销


但是在这种情况下,开销非常小,比这里的任何其他方法都要少。STATIC告诉SQLServer在tempdb中具体化结果,然后对其进行迭代。对于这样的小列表,它是最佳解决方案。

我通常使用以下方法

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
您可以尝试以下操作:

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

在这里使用过程编程语言Python与数据库建立连接,并在那里进行循环。这样,您也可以执行复杂的循环

与数据库建立连接 导入pyodbc conn=pyodbc.connect' 驱动程序={ODBC驱动程序13 for SQL Server}; Server=serverName; 数据库=DBname; UID=用户名; PWD=密码; ' 光标=连接光标 运行sql代码 对于[4,7,12,22,19]中的id: 游标。执行' exec p_MyInnerProcedure{} '.formatid
上述方法相当于或慢于在表变量上声明的游标。这当然不是更快。它比在常规用户表上用默认选项声明的游标要快。@Peter,啊,是的,你是正确的,我错误地认为使用游标意味着常规用户表,而不是表变量。。我进行了编辑,以明确区别我希望有一种更优雅的方式,但我认为这将是我能得到的最接近的方式:最终使用了混合使用选择/联合和示例中的光标。谢谢@john:如果你使用2008,你可以做一些类似于插入@ids值4、7、12、22、19的事情仅供参考,像这样的内存表通常比游标快,虽然对于5个值我几乎看不出有什么区别,但我喜欢它们的最大原因是我发现它们的语法与你在应用程序代码中找到的语法相似,然而,在我看来,游标是相对不同的。虽然它实际上对性能的影响很小,但我想指出的是,它会遍历定义空间内的所有数字。下面的解决方案存在,请从@Ids中选择*。。。逻辑上更合理、更优雅。我会这样做列表声明:@list='4,7,12,22,19'+','-所以很明显,列表必须以逗号结尾,没有逗号是不行的!。相关的,如果你需要在一个非整数列表上迭代,比如varchars,cursor解决方案:在看了所有这些TSQL示例之后,我刚刚得出了这个结论,当我看到你的答案时,我正在前往PyCharm的路上。
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