PL / SQL查询以逗号分隔的字符串

时间:2011-08-12 13:26:20

标签: oracle plsql oracle-apex

我正在Oracle APEX中开发一个应用程序。我有一个用户ID的字符串,用逗号分隔,看起来像这样,

45,4932,20,19

此字符串存储为

:P5_USER_ID_LIST

我想要一个查询,该查询将查找此列表中的所有用户,我的查询如下所示

SELECT * FROM users u WHERE u.user_id IN (:P5_USER_ID_LIST);

我一直收到Oracle错误:数字无效。但是,如果我将字符串硬编码到查询中就可以了。像这样:

SELECT * FROM users u WHERE u.user_id IN (45,4932,20,19);

任何人都知道为什么这可能是一个问题?

10 个答案:

答案 0 :(得分:10)

绑定变量绑定 a 值,在本例中为字符串'45,4932,20,19'。您可以使用Randy建议的动态SQL和连接,但是您需要非常小心,用户无法修改此值,否则您会遇到SQL注入问题。

更安全的路线是将ID放入PL / SQL过程中的Apex集合中:

declare
    array apex_application_global.vc_arr2;
begin
    array := apex_util.string_to_table (:P5_USER_ID_LIST, ',');
    apex_collection.create_or_truncate_collection ('P5_ID_COLL');
    apex_collection.add_members ('P5_ID_COLL', array);
end;

然后将您的查询更改为:

SELECT * FROM users u WHERE u.user_id IN 
(SELECT c001 FROM apex_collections
 WHERE collection_name = 'P5_ID_COLL')

答案 1 :(得分:3)

更简单的解决方案是使用instr

SELECT * FROM users u 
WHERE instr(',' || :P5_USER_ID_LIST ||',' ,',' || u.user_id|| ',', 1) !=0;

招数:

',' || :P5_USER_ID_LIST ||','

制作字符串,45,4932,20,19,

',' || u.user_id|| ','

要{ie} ,32,并避免选择32中的,4932,

答案 2 :(得分:1)

我已经多次面对这种情况了,这就是我用过的东西:

SELECT * 
  FROM users u 
 WHERE ','||to_char(:P5_USER_ID_LIST)||',' like '%,'||to_char(u.user_id)||',%'

我已经使用了类似的操作符,但你必须在这里稍微注意一个方面:你的项目P5_USER_ID_LIST必须是“,45,4932,20,19”,这样就可以比较确切的数字“',45, “”。

当像这样使用它时,选择不会误认为:5,15,155,55。

尝试一下,让我知道它是怎么回事;)

干杯, 亚历

答案 3 :(得分:0)

创建本机查询而不是使用“createQuery / createNamedQuery”

答案 4 :(得分:0)

这是一个问题的原因是你不能只按照你想要的方式绑定一个列表,而且几乎每个人都会犯这个错误,因为他们正在学习Oracle(可能还有SQL!)。

当你绑定字符串'32,64,128'时,它实际上变成了一个类似的查询:

select ...
from t
where t.c1 in ('32,64,128')

对Oracle而言,这完全不同于:

select ...
from t
where t.c1 in (32,64,128)

第一个示例在列表中有一个字符串值,第二个示例在列表中有3个数字。您收到无效数字错误的原因是因为Oracle尝试将字符串'32,64,128'转换为数字,由于字符串中的逗号,它无法执行此操作。

这个“我如何绑定列表”这一问题的变体最近出现了很多次。

一般而言,没有诉诸任何PLSQL,担心SQL注入或者没有正确绑定查询,你可以使用这个技巧:

with bound_inlist
  as
  (
  select
    substr(txt,
           instr (txt, ',', 1, level  ) + 1,
           instr (txt, ',', 1, level+1) - instr (txt, ',', 1, level) -1 )
           as token
    from (select ','||:txt||',' txt from dual)
  connect by level <= length(:txt)-length(replace(:txt,',',''))+1
  )
  select *
from bound_inlist a, users u
where a.token = u.id;

答案 5 :(得分:0)

如果可能,最好的想法可能是不将您的用户ID存储在csv中!将它们放在表中或使数组等失败。您不能将csv字段绑定为数字。

答案 6 :(得分:0)

请不要使用:WHERE','|| to_char(:P5_USER_ID_LIST)||','喜欢'%,'|| to_char(u.user_id)||',%',因为你会强制一个完整的表扫描尽管使用users表,但可能没有那么多,因此影响会很小,但是对于企业环境中的其他表,这是一个问题。

编辑:我已经整理了一个脚本来演示正则表达式方法和通配符之类的方法之间的差异。正则表达式不仅更快,而且更强大。

-- Create table
create table CSV_TEST
(
  NUM NUMBER not null,
  STR VARCHAR2(20)
);


create sequence csv_test_seq;

begin
  for j in 1..10 loop
    for i in 1..500000 loop
     insert into csv_test( num, str ) values ( csv_test_seq.nextval, to_char( csv_test_seq.nextval ));
    end loop;
    commit;
  end loop;
end;
/

-- Create/Recreate primary, unique and foreign key constraints 
alter table CSV_TEST
  add constraint CSV_TEST_PK primary key (NUM)
  using index ;
alter table CSV_TEST
  add constraint CSV_TEST_FK unique (STR)
  using index;

select sysdate from dual;
select *
from csv_test t
where t.num in ( Select Regexp_Substr('100001, 100002,   100003   ,      100004, 100005','[^,]+', 1, Level) From Dual
                 Connect By Regexp_Substr('100001, 100002,100003, 100004, 100005', '[^,]+', 1, Level) Is Not Null);
select sysdate from dual;

select *
from csv_test t
where ('%,' || '100001,100002,   100003,  100004  ,100005' || ',%') like '%,' || num || ',%';
select sysdate from dual;
select *
from csv_test t
where t.num in ( Select Regexp_Substr('100001, 100002,   100003   ,      100004, 100005','[^,]+', 1, Level) From Dual
                 Connect By Regexp_Substr('100001, 100002,100003, 100004, 100005', '[^,]+', 1, Level) Is Not Null);
select sysdate from dual;

select *
from csv_test t
where ('%,' || '100001,100002,   100003,  100004  ,100005' || ',%') like '%,' || num || ',%';
select sysdate from dual;

drop table csv_test;
drop sequence csv_test_seq;

答案 7 :(得分:0)

Tony Andrews的解决方案适合我。该过程应添加到“页面处理”&gt;&gt; “提交后”&gt;&gt; “进程”。

答案 8 :(得分:0)

当您将用户ID存储为字符串时,可以使用getitem轻松匹配字符串,如下所示:

Like

例如

SELECT * FROM users u WHERE u.user_id LIKE '%'||(:P5_USER_ID_LIST)||'%'

您的查询肯定会返回与“用户”表匹配的1个用户ID中的任何一个

这肯定可以解决您的问题,请尽情享受

答案 9 :(得分:-1)

您需要将其作为动态SQL运行。

创建整个字符串,然后动态运行。