代码之家  ›  专栏  ›  技术社区  ›  John

T-SQL:通过已知值数组循环

  •  73
  • John  · 技术社区  · 15 年前

    以下是我的场景:

    假设我有一个存储过程,在该存储过程中,我需要调用一组特定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),能够在一行中列出所有ID…性能不应该是一个大问题

    6 回复  |  直到 8 年前
        1
  •  88
  •   Adam Robinson    15 年前
    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
    
        2
  •  39
  •   Charles Bretana    8 年前

    在这个场景中,我要做的是创建一个表变量来保存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
    

    上述任何一种方法都比光标(针对常规用户表声明)快得多。表值变量的重复性很差,因为如果使用不当(对于行数很大的非常宽的表),它们不会被执行。但是,如果您只使用它们保存一个键值或一个4字节的整数,并且使用一个索引(在本例中),那么它们的速度非常快。

        3
  •  16
  •   Noctis    8 年前

    使用静态光标变量和 split function :

    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中具体化结果,然后迭代该结果。对于这样的小列表,这是最佳解决方案。

        4
  •  4
  •   kristof    15 年前

    我通常使用以下方法

    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
    
        5
  •  3
  •   AjV Jsy    11 年前

    您可以尝试如下操作:

    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
    
        6
  •  2
  •   James A Mohler Nika    12 年前
    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