【问题标题】:How to input an array parameter of values to Firebird Stored Procedure?如何将值的数组参数输入到 Firebird 存储过程?
【发布时间】:2013-03-28 07:06:00
【问题描述】:

我想向Firebird存储过程输入一个数组参数的ID。

:INPUT_LIST_ID = [1, 2, 12, 45, 75, 45]

我需要执行这条 SQL 命令:

SELECT *
FROM CITY
WHERE ID_CITY IN (:INPUT_LIST_ID)

有可能吗? 谢谢!

【问题讨论】:

    标签: sql arrays parameters firebird


    【解决方案1】:

    AFAIK 不,那是不可能的。虽然 Firebird 确​​实有数组数据类型,但对它的支持是基本的,通常不建议使用数组。我认为最简单的解决方案是将数组作为(逗号分隔)字符串传递,然后使用for execute statement 语句获取结果集,类似于

    create procedure CITY (INPUT_LIST_ID varchar(1024)) 
    returns( ... )
    as
    begin
      for execute statement
        'select ... from T where ID_CITY IN ('|| INPUT_LIST_ID ||')' into ...
      do begin
         suspend;
      end
    end
    

    这意味着您用来获取结果的语句也会发生变化,而不是WHERE,您将使用存储过程的参数CITY

    SELECT * FROM CITY('1, 2, 12, 45, 75, 45')
    

    发送参数列表的另一个选项是使用global temporary table。这样做的好处是您可以在不超过最大允许语句大小的情况下发送大量 ID,但设置调用需要更多工作...

    create global temporary table SP_CITY_PARAMS (
      id int not null primary key
    )
    on commit delete rows;
    
    create procedure CITY
    returns( ... )
    as
    begin
      for select ... from T where ID_CITY IN (
          select id from SP_CITY_PARAMS
      ) into ...
      do begin
         suspend;
      end
    end
    

    【讨论】:

    • 我认为它有效,但我的数据库中有超过 10000 个城市。
    • @dataol 你想全部通过吗?为什么不直接加入包含这些城市的表格?
    • @MarkRotteveel 不是全部,只是选定的记录。但用户可以选择 1 个或 10.000 个城市。
    • @dataol 考虑使用global temporary table,插入选定的城市,加入反对。
    • @MarkRotteveel 好的。我会试试看。谢谢!
    【解决方案2】:

    你也可以这样使用:

    SELECT *
    FROM CITY
    WHERE ID_CITY IN (SELECT ID FROM GetIntegerList('1, 2, 12, 45, 75, 45'))
    

    您必须创建一个名为“GetIntegerList”的新 Firebird 过程,如下所示:

    CREATE OR ALTER PROCEDURE "GETINTEGERLIST"("AINTEGERLIST" VARCHAR(32000))
    returns (
      ID integer
    )
    as
      declare variable IntegerList varchar(32000);
      declare variable CommaPos integer;
      declare variable IntegerVal varchar(10);
    begin
      IntegerList = AIntegerList || ' ';
      CommaPos = Position(',', IntegerList);
    
      while (CommaPos > 0) do
      begin
        IntegerVal = Trim(SubString(IntegerList from 1 for CommaPos - 1));
    
        if (Char_Length(IntegerVal) > 0) then
        begin
          if (IntegerVal similar to '[0-9]*') then
          begin
            ID = Cast(IntegerVal as integer);
            suspend;
          end
        end
    
        if (Char_Length(IntegerList) > CommaPos) then
          IntegerList = SubString(IntegerList from CommaPos + 1);
        else
          IntegerList = '';
    
        CommaPos = Position(',', IntegerList);
      end
    
      IntegerList = Trim(IntegerList);
    
      if (Char_Length(IntegerList) > 0) then
      begin
        if (IntegerList similar to '[0-9]*') then
        begin
          ID = Cast(IntegerList as integer);
          suspend;
        end
      end
    end
    

    注意,这是在 Firebird 2.5.2 中完成的。

    【讨论】:

      【解决方案3】:

      试试这个:

          SELECT *
          FROM CITY
          WHERE '/city1/city2/city.../' containing '/' || ID_CITY || '/';
      

      【讨论】:

        【解决方案4】:

        如果您使用 Firebird 1.5(它也应该在更高版本上工作),您可以使用我制作的这个简单函数将单个字符串转换为整数数组:

        create or alter procedure INTEGER_LIST (
            input varchar(4096))
        returns (
            INT_VALUE integer)
        as
        declare variable CHAR_COUNT integer;
        declare variable PARAM_LENGTH integer;
        declare variable READ_VALUE char(1);
        declare variable CURRENT_INTEGER varchar(20);
        begin
            param_length = strlen(input);
            char_count = 0;
            current_integer = '';
            while (char_count < param_length) do begin
                char_count = :char_count + 1;
                read_value = substr(:input, :char_count, :char_count);
                if (:read_value <> ',') then begin
                    current_integer = :current_integer || :read_value;
                end else if (:read_value <> ' ') then  begin
                    int_value = cast(:current_integer as integer);
                    current_integer = '';
                    suspend;
                end
        
                if (:char_count = :param_length) then begin
                    int_value = cast(:current_integer as integer);
                    suspend;
                end
            end
        end
        

        用法

        select int_value from integer_list('1,2,3,4, 5, 200, 1, 598415, 2')

        会返回这个:

        INT_VALUE
        1
        2
        3
        4
        5
        200
        1
        598415
        2
        

        【讨论】:

        • 很好,谢谢!但它只适用于短字符串(如 50 个字符),并且在长字符串时崩溃 =(((( 在这种情况下你能帮帮我吗?
        • 很抱歉,但幸运的是我不再使用 firebird,但不管字符串长度如何,它都应该可以工作,您发现了什么问题?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-28
        • 2014-04-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多