Как обозначить значение столбца, разделенного точкой с запятой, для передачи в оператор IF в функции в Oracle DB

#sql #oracle #oracle12c

#sql #Oracle #oracle12c

Вопрос:

У меня есть таблица с именем ‘config’, и когда я запрашиваю ее следующим образом:

SELECT value FROM config WHERE property = 'SPECIAL_STORE_ID'

его ответ будет: 59216;131205;76707;167206 //... (1)

Я хочу обозначить вышеуказанные значения, используя точку с запятой в качестве разделителя, а затем использовать их в операторе пользовательской функции IF для сравнения, что-то вроде этого:

 IF in_store_id exists in (<delimited response from (1) above>)//...(2)
THEN do some stuff
  

где in_store_id находится параметр, переданный функции

Возможно ли это сделать как однострочное в (2) выше?

Я на Oracle 12c

Ответ №1:

Однострочный? Я так не думаю, но — если вас устраивает что-то подобное, хорошо.

 SQL> select * From config;

VALUE          PROPERTY
-------------- ----------------
7369;7499;7521 SPECIAL_STORE_ID

SQL> declare
  2    in_store_id varchar2(20) := 7369;
  3    l_exists    number;
  4  begin
  5    select instr(value, ';' || in_store_id || ';')
  6      into l_exists
  7      from config
  8      where property = 'SPECIAL_STORE_ID';
  9
 10    if l_exists > 0 then
 11       dbms_output.put_line('that STORE_ID exists in the value');
 12    else
 13       dbms_output.put_line('that STORE_ID does not exist in the value');
 14    end if;
 15  end;
 16  /
that STORE_ID exists in the value

PL/SQL procedure successfully completed.

SQL>
  

Комментарии:

1. Если я не ошибаюсь, это будет работать некорректно в случае, когда in_store_id будет только часть некоторого идентификатора внутри value like in_store_id is 1 и value is 123;456;789 . regexp_instr(value, '(^|;)'||in_store_id||'(;|$)') может быть лучшим выбором

2. Вы также можете избежать регулярных выражений, добавив разделитель с обеих сторон строки и выполнив поиск ‘;myval;’. Если производительность является требованием, то, очевидно, модель данных нуждается в пересмотре

3. Спасибо вам обоим. Исправлено, строка # 5.

4. @AndrewSayer хороший момент, но все равно не будет работать, если идентификатор является первым или последним в значении. Однако должно работать, если оба value и in_store_id будут обернуты разделителем с обеих сторон

Ответ №2:

Если ответ с разделителями является коллекцией, вы можете использовать member of для проверки, содержит ли коллекция идентификатор или нет, как

 create or replace procedure test_procedure2(p_property in varchar2, p_id in varchar2) is
    type test_t is table of varchar2(20);
    l_ids test_t;
begin
    select regexp_substr(value, '[^;] ', 1, level) bulk collect into l_ids
    from (select value from config where property = p_property)
    connect by level <= regexp_count(value, ';') 1;
    
    if(p_id member of (l_ids)) then
        dbms_output.put_line('Do stuff for '||p_property||' '||p_id);
    end if;
end;
/
  

или сделайте это без сбора с промежуточным выбором, например

 create or replace procedure test_procedure1(p_property in varchar2, p_id in varchar2) is
    l_flag number(3);
begin
    select count(1) into l_flag from dual where p_id in (
        select regexp_substr(value, '[^;] ', 1, level)
        from (select value from config where property = p_property)
        connect by level <= regexp_count(value, ';') 1
    );
    
    if(l_flag > 0) then
        dbms_output.put_line('Do stuff for '||p_property||' '||p_id);
    end if;
end;
/
  

См. fiddle

Комментарии:

1. Спасибо за ваш ответ. Мне пришлось гуглить connect by , чтобы узнать, что это такое 🙂 -узнал кое-что классное. Ответ Литтлфута менее сложный — я соглашусь с этим

2. Конечно, я также нахожу ответ Литтлфута более кратким в случае, когда вам просто нужно проверить, представлен ли идентификатор в строке или нет. По какой-то причине я просто решил, что вам также нужно иметь возможность маркировать строку для некоторых других целей