Oracle SQL: выберите regexp_substr, ожидается предложение into

Я пытаюсь разбить строку с разделителями-запятыми, используя regexp_substr, но получаю ошибку «ожидается предложение into»:

 select regexp_substr(improv,'[^,]+', 1, level) from dual
 connect by regexp_substr(improv, '[^,]+', 1, level) is not null;

«improv» — это переменная типа varchar(100), и я запускаю приведенный выше оператор внутри блока PLSQL.


person Matin    schedule 18.01.2018    source источник
comment
Сообщение об ошибке достаточно ясное: вам нужно сохранить результат вашего запроса INTO в некоторой переменной в блоке PL/SQL.   -  person Aleksej    schedule 18.01.2018
comment
@Алексей: Ага. Я объявил тип переменной таблицы и изменил код на select regexp_substr(improv,'[^,]+', 1, level) from dual connect by regexp_substr(improv, '[^,]+', 1, level) is not null into my_table;, но получил команду sql ошибки компиляции, которая не закончилась должным образом между нулем и в   -  person Matin    schedule 18.01.2018
comment
Используйте 1_   -  person Wernfried Domscheit    schedule 18.01.2018
comment
Регулярное выражение формы '[^,]+' не допускает элементов списка NULL и возвращает неправильный элемент, если встречается NULL. Дополнительную информацию см. здесь: stackoverflow.com/a/31464699/2543416. Вместо этого используйте эту форму (11g и более поздние версии): regexp_substr(improv,'(.*?)(,|$)', 1, level, NULL, 1)   -  person Gary_W    schedule 18.01.2018


Ответы (2)


В PL/SQL вам нужно вывести запрос SQl INTO переменной.

Однако, поскольку этот запрос будет генерировать несколько строк, вы, вероятно, захотите использовать BULK COLLECT INTO, а не только INTO, и поместить вывод в определяемую пользователем коллекцию или VARRAY (примером которой является SYS.ODCIVARCHAR2LIST). Примечание: вы не можете использовать оператор MEMBER OF с VARRAY):

DECLARE
  list_of_improvs SYS.ODCIVARCHAR2LIST;
BEGIN
  SELECT regexp_substr(improv,'[^,]+', 1, level)
  BULK COLLECT INTO list_of_improvs
  FROM   DUAL
  CONNECT BY regexp_substr(improv, '[^,]+', 1, level) is not null;
END;
/

Обновление:

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

DECLARE
  list_of_improvs SYS.ODCIVARCHAR2LIST;
BEGIN
  SELECT regexp_substr(improv,'[^,]+', 1, level)
  BULK COLLECT INTO list_of_improvs
  FROM   DUAL
  CONNECT BY regexp_substr(improv, '[^,]+', 1, level) is not null;

  FOR i IN 1 .. list_of_improvs.COUNT LOOP
    DBMS_OUTPUT.PUT_LINE( improvs(i) );
  END LOOP;

  update line_cap
    set   cap_up = list_of_improvs(1)
    where id IN ( SELECT Column_Value
                  FROM TABLE( list_of_improvs ) );
END;
/

Вы не можете использовать IN напрямую с коллекцией или VARRAY и должны использовать выражение коллекции TABLE() во вложенном запросе, чтобы получить значения.

Если вы используете определяемую пользователем коллекцию SQL, то есть определенную с помощью такого оператора:

CREATE TYPE StringList IS TABLE OF VARCHAR2(4000);

Затем вы можете использовать оператор MEMBER OF:

DECLARE
  list_of_improvs StringList;
BEGIN
  -- as above

  update line_cap
    set   cap_up = list_of_improvs(1)
    where id MEMBER OF list_of_improvs;
END;
/

Но вы не можете использовать оператор MEMBER OF с VARRAYs (например, SYS.ODCIVARCHAR2LIST).

Однако для этого вам не нужен PL/SQL (и исключить дорогостоящие переключения контекста между областями выполнения PL/SQL и SQL) и можно просто использовать оператор MERGE, например:

MERGE INTO line_cap dst
USING (
  SELECT MIN( value ) KEEP ( DENSE_RANK FIRST ORDER BY ROWNUM ) OVER () AS first_value,
         value
  FROM   (
    SELECT regexp_substr(improv,'[^,]+', 1, level) AS value
    FROM   DUAL
    CONNECT BY regexp_substr(improv, '[^,]+', 1, level) is not null
  )
) src
ON ( src.value = dst.id )
WHEN MATCHED THEN
  UPDATE
    SET cap_up = first_value;
person MT0    schedule 18.01.2018
comment
@MTO: могу я спросить, каким будет имя столбца таблицы, объявленной как SYS.ODCIVARCHAR2LIST? - person Matin; 18.01.2018
comment
@user1777530 user1777530 COLUMN_VALUE — это автоматически сгенерированное имя столбца для всех выражений коллекции таблиц для пользовательских коллекций и VARRAYs (примитивных типов). - person MT0; 18.01.2018
comment
@MTO: как я могу получить из этого числа? Я попробовал ODCINUMBERLIST вместо ODCIVARCHAR2LIST, но по-прежнему получаю несогласованные типы данных: ожидаемое число NUMBER получило ошибку SYS.ODCINUMBERLIST для list_of_improvs, когда я пробовал этот оператор обновления. идентификатор - это число: update line_cap set cap_up = list_of_improvs(1) where id in list_of_improvs; - person Matin; 18.01.2018

Я разместил это здесь вместо комментария к ответу MT0, так как комментарии не работают для форматирования. Во всяком случае, вот полный пример, использующий ответ MT0 для загрузки массива, а затем прокручивающий его для отображения содержимого. Это покажет вам, как получить доступ к содержимому списка. Отдайте должное MT0 за ответ на ваш первоначальный вопрос.

DECLARE
  list_of_improvs SYS.ODCIVARCHAR2LIST;
  i number;
BEGIN
  SELECT regexp_substr('1,2,3,4,5','(.*?)(,|$)', 1, level, NULL, 1)
  BULK COLLECT INTO list_of_improvs
  FROM   dual
  CONNECT BY level <= regexp_count('1,2,3,4,5', ',') + 1;

  i := list_of_improvs.FIRST;  -- Get first element of array
  while i is not null LOOP
    DBMS_OUTPUT.PUT_LINE(list_of_improvs(i));
    i := list_of_improvs.NEXT(i);  -- Get next element of array
  END LOOP;
END;
/
person Gary_W    schedule 18.01.2018
comment
BULK COLLECT INTO всегда будет генерировать коллекцию (или VARRAY) с первым индексом 1 и увеличением на 1, чтобы вы могли упростить цикл до FOR i IN 1 .. list_of_improvs.COUNT LOOP и исключить переменную i и назначения FIRST и NEXT(i). Однако, если вход представляет собой ассоциативный массив PL/SQL, который может содержать разреженные (или нечисловые) индексы, то использование FIRST и NEXT(i) определенно является правильным методом. - person MT0; 19.01.2018