如何将值的数组参数输入到 Firebird 存储过程?
Posted
技术标签:
【中文标题】如何将值的数组参数输入到 Firebird 存储过程?【英文标题】:How to input an array parameter of values to Firebird Stored Procedure? 【发布时间】: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)
有可能吗? 谢谢!
【问题讨论】:
【参考方案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,但不管字符串长度如何,它都应该可以工作,您发现了什么问题?以上是关于如何将值的数组参数输入到 Firebird 存储过程?的主要内容,如果未能解决你的问题,请参考以下文章