Разделение разделенной запятой строки в PL / SQL хранимой процедуре
У меня CSV строка 100.01.200.02.300.03, которую мне нужно передать в хранимую процедуру PL / SQL в Oracle. Внутри proc мне нужно вставить эти значения в столбец Number в таблице.
Для этого я получил рабочий подход отсюда:
Как лучше всего разбить строки csv в oracle 9i
- Шаблоны C ++ Угловые скобки Pitfall - Что такое C ++ 11?
- Преобразование разделенной запятой строки в массив в PL / SQL
- Токенизация строк в C
- Ищете четкое определение того, что такое «токенизатор», «парсер» и «лексеры» и как они связаны друг с другом и используются?
- Как сделать токенизацию строки в C ++?
[2] Использование SQL-соединения по уровню.].
Теперь у меня другое требование. Мне нужно передать две строки CSV [равные по длине] как входные данные в PL / SQL, хранящиеся в proc.And, мне нужно разбить эту строку и вставить каждое значение из двух строк CSV в два разных столбца в таблице. Не могли бы вы позволить мне знаете, как это сделать?
Пример входов CSV: mystring varchar2 (2000): = ‘0,75, 0,64, 0,56, 0,45’;
myAmount varchar2 (2000): = ‘0,25, 0,5, 0,65, 0,8’;
Значения myString будут отображаться в столбцах A и myAmount в столбце B в таблице.
Не могли бы вы сообщить мне, как этого добиться?
Благодарю.
- Как читать ввод по-символам в Java?
- Как лучше всего разбить строки csv в oracle 9i
- Тонирование и сортировка с помощью XSLT 1.0
- Как использовать stringstream для разделения разделенных запятыми строк
- Как получить токен из Lucene TokenStream?
- Каков самый простой / лучший / самый правильный способ перебора символов строки в Java?
- Есть ли функция для разделения строки в PL / SQL?
- Ошибка Tokenizing: java.util.regex.PatternSyntaxException, оборванный метасимвол '*'
Это должно делать то, что вы ищете. Предполагается, что ваш список всегда будет просто числом. Если это не так, просто измените ссылки на DBMS_SQL.NUMBER_TABLE на тип таблицы, который работает для всех ваших данных:
CREATE OR REPLACE PROCEDURE insert_from_lists( list1_in IN VARCHAR2, list2_in IN VARCHAR2, delimiter_in IN VARCHAR2 := ',' ) IS v_tbl1 DBMS_SQL.NUMBER_TABLE; v_tbl2 DBMS_SQL.NUMBER_TABLE; FUNCTION list_to_tbl ( list_in IN VARCHAR2 ) RETURN DBMS_SQL.NUMBER_TABLE IS v_retval DBMS_SQL.NUMBER_TABLE; BEGIN IF list_in is not null THEN /* || Use lengths loop through the list the correct amount of times, || and substr to get only the correct item for that row */ FOR i in 1 .. length(list_in)-length(replace(list_in,delimiter_in,''))+1 LOOP /* || Set the row = next item in the list */ v_retval(i) := substr ( delimiter_in||list_in||delimiter_in, instr(delimiter_in||list_in||delimiter_in, delimiter_in, 1, i ) + 1, instr (delimiter_in||list_in||delimiter_in, delimiter_in, 1, i+1) - instr (delimiter_in||list_in||delimiter_in, delimiter_in, 1, i) -1 ); END LOOP; END IF; RETURN v_retval; END list_to_tbl; BEGIN -- Put lists into collections v_tbl1 := list_to_tbl(list1_in); v_tbl2 := list_to_tbl(list2_in); IF v_tbl1.COUNT <> v_tbl2.COUNT THEN raise_application_error(num => -20001, msg => 'Length of lists do not match'); END IF; -- Bulk insert from collections FORALL i IN INDICES OF v_tbl1 insert into tmp (a, b) values (v_tbl1(i), v_tbl2(i)); END insert_from_lists;
Вот хорошее решение:
FUNCTION comma_to_table(iv_raw IN VARCHAR2) RETURN dbms_utility.lname_array IS ltab_lname dbms_utility.lname_array; ln_len BINARY_INTEGER; BEGIN dbms_utility.comma_to_table(list => iv_raw ,tablen => ln_len ,tab => ltab_lname); FOR i IN 1 .. ln_len LOOP dbms_output.put_line('element ' || i || ' is ' || ltab_lname(i)); END LOOP; RETURN ltab_lname; END;
Источник: CSV – значения, разделенные запятыми, – и PL / SQL (ссылка больше не действительна)
Я использую apex_util.string_to_table для синтаксического анализа строк, но вы можете использовать другой парсер, если хотите. Затем вы можете вставить данные, как в этом примере:
declare myString varchar2(2000) :='0.75, 0.64, 0.56, 0.45'; myAmount varchar2(2000) :='0.25, 0.5, 0.65, 0.8'; v_array1 apex_application_global.vc_arr2; v_array2 apex_application_global.vc_arr2; begin v_array1 := apex_util.string_to_table(myString, ', '); v_array2 := apex_util.string_to_table(myAmount, ', '); forall i in 1..v_array1.count insert into mytable (a, b) values (v_array1(i), v_array2(i)); end;
Apex_util доступен от Oracle 10G и далее. До этого он назывался htmldb_util и не был установлен по умолчанию. Если вы не можете использовать это, вы можете использовать синтаксический анализатор строк, который я написал много лет назад и опубликовал здесь .
Я не уверен, соответствует ли это вашей версии oracleа. На моих 10g я могу использовать конвейерные функции таблицы:
set serveroutput on create type number_list as table of number; -- since you want this solution create or replace function split_csv (i_csv varchar2) return number_list pipelined is mystring varchar2(2000):= i_csv; begin for r in ( select regexp_substr(mystring,'[^,]+',1,level) element from dual connect by level <= length(regexp_replace(mystring,'[^,]+')) + 1 ) loop --dbms_output.put_line(r.element); pipe row(to_number(r.element, '999999.99')); end loop; end; / insert into foo select column_a,column_b from (select column_value column_a, rownum rn from table(split_csv('0.75, 0.64, 0.56, 0.45'))) a ,(select column_value column_b, rownum rn from table(split_csv('0.25, 0.5, 0.65, 0.8'))) b where a.rn = b.rn ;
CREATE OR REPLACE PROCEDURE insert_into ( p_errcode OUT NUMBER, p_errmesg OUT VARCHAR2, p_rowsaffected OUT INTEGER ) AS v_param0 VARCHAR2 (30) := '0.25,2.25,33.689, abc, 99'; v_param1 VARCHAR2 (30) := '2.65,66.32, abc-def, 21.5'; BEGIN FOR i IN (SELECT COLUMN_VALUE FROM TABLE (SPLIT (v_param0, ','))) LOOP INSERT INTO tempo (col1 ) VALUES (i.COLUMN_VALUE ); END LOOP; FOR i IN (SELECT COLUMN_VALUE FROM TABLE (SPLIT (v_param1, ','))) LOOP INSERT INTO tempo (col2 ) VALUES (i.COLUMN_VALUE ); END LOOP; END;
создать или заменить procedure pro_ss(v_str varchar2)
как
v_str1 varchar2(100); v_comma_pos number := 0; v_start_pos number := 1; begin loop v_comma_pos := instr(v_str,',',v_start_pos); if v_comma_pos = 0 then v_str1 := substr(v_str,v_start_pos); dbms_output.put_line(v_str1); exit; end if; v_str1 := substr(v_str,v_start_pos,(v_comma_pos - v_start_pos)); dbms_output.put_line(v_str1); v_start_pos := v_comma_pos + 1; end loop; end; / call pro_ss('aa,bb,cc,dd,ee,ff,gg,hh,ii,jj');
outout: aa bb cc dd ee ff gg hh ii jj
Уже было предоставлено много хороших решений. Однако, если текст предоставлен в очень простом формате с разделителями-запятыми или аналогичным, и скорость имеет важное значение, то у меня есть для вас решение с функцией TABLE
(в PL / SQL). Я также представил некоторые другие решения.
Подробнее о записи блога в разделе « Анализ CSV» см. В нескольких столбцах .
Что касается connect by
использованию, этот подход должен работать на вас:
select regexp_substr('SMITH,ALLEN,WARD,JONES','[^,]+', 1, level) from dual connect by regexp_substr('SMITH,ALLEN,WARD,JONES', '[^,]+', 1, level) is not null;