PL / SQL-запрос В строке с разделителями-запятыми - PullRequest
9 голосов
/ 12 августа 2011

Я занимаюсь разработкой приложения в Oracle APEX.У меня есть строка с идентификаторами пользователей с разделителями-запятыми, которая выглядит следующим образом:

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 ]

11 голосов
/ 12 августа 2011

Переменная bind связывает значение a , в данном случае строку «45, 4932,20,19». Вы можете использовать динамический SQL и конкатенацию, как это было предложено Рэнди, но вам нужно быть очень осторожным, чтобы пользователь не смог изменить это значение, иначе у вас возникнет проблема с SQL-инъекцией.

Более безопасный путь - поместить идентификаторы в коллекцию Apex в процессе PL / SQL:

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')
3 голосов
/ 12 августа 2011

Более простое решение - использовать 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|| ','

чтобы иметь то есть ,32, и избегать выбора 32, находящегося в ,4932,

1 голос
/ 25 августа 2011

Я сталкивался с этой ситуацией несколько раз, и вот что я использовал:

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

я использовал оператор like, но вы должны быть немного осторожны с одним аспектом: ваш элемент P5_USER_ID_LIST должен быть", 45,4932,20,19," так что like будет сравниваться с точным числом "', 45,'".

При использовании этого слова выбор не будет ошибкой, скажем: 5 с15, 155, 55.

Попробуйте и дайте мне знать, как это происходит;)

Приветствия, Алекс

0 голосов
/ 08 декабря 2018

Поскольку вы храните идентификаторы пользователей как строки, так что вы можете легко сопоставлять строки, используя Like как ниже

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

Например

:P5_USER_ID_LIST  = 45,4932,20,19

Ваш запрос обязательно вернет любой из1 идентификатор пользователя, который соответствует таблице пользователей

Это, безусловно, решит вашу проблему, наслаждайтесь

0 голосов
/ 10 сентября 2014

Решение от Тони Эндрюса работает на меня. Процесс должен быть добавлен в «Обработка страницы» >> «После отправки» >> «Процессы».

0 голосов
/ 06 февраля 2014

Пожалуйста, не используйте: WHERE ',' || to_char (: P5_USER_ID_LIST) || ',' подобно '%,' || to_char (u.user_id) || ',%', потому что вы форсируете полную таблицу сканируйте, хотя с таблицей пользователей у вас может быть не так много, так что влияние будет низким, но для других таблиц в корпоративной среде это проблема.

РЕДАКТИРОВАТЬ: я собрал скрипт, чтобы продемонстрировать различия между методом регулярных выражений и методом, подобным символу подстановки. Мало того, что регулярное выражение работает быстрее, но и намного более надежно.

-- 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;
0 голосов
/ 24 сентября 2011

Если возможно, лучшая идея - не хранить ваши идентификаторы в CSV!Поместите их в таблицу или, если это не так, массив и т. Д. Вы не можете связать поле CSV как число.

0 голосов
/ 12 августа 2011

Причина, по которой это проблема, заключается в том, что вы не можете просто связать список в списке так, как вам хочется, и почти все делают эту ошибку хотя бы один раз, когда они изучают 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)

В первом примере естьодно строковое значение в списке in, а второе имеет 3 числа в списке in.Причина, по которой вы получаете ошибку неверного числа, заключается в том, что 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;
0 голосов
/ 12 августа 2011

Создайте собственный запрос вместо использования "createQuery / createNamedQuery"

0 голосов
/ 12 августа 2011

вам нужно будет запустить это как динамический SQL.

создать всю строку, а затем запустить ее динамически.

...