Как лучше всего разделить строки csv в оракуле 9i

XML, JSON, INI.
у них всех есть свои достоинства и недостатки.
В контексте приложения, я чувствую, что уровень абстракции является важной вещью.
, Если можно выбрать способ структурировать данные, которые являются хорошим компромиссом между человеческой удобочитаемостью и как Вы хотите получить доступ/абстрагировать к данным в коде, Вы являетесь золотыми.

Мы главным образом используем XML, где я работаю, и я не могу действительно полагать, что конфигурационный файл загрузился в кэш как объекты, когда сначала считанный или после того, как это было записано в, и затем абстрагировало далеко от остальной части программы, действительно не так большая часть хита ни на ЦП, ни на дисковом пространстве.
И это довольно читаемо также, пока Вы структурируете право файла.

И все языки на всех платформах поддерживает XML через некоторые довольно общие библиотеки.

8
задан APC 15 September 2010 в 09:08
поделиться

5 ответов

Вот строковый токенизатор для Oracle, который немного проще, чем эта страница, но я не знаю, настолько ли он быстр:

create or replace function splitter_count(str in varchar2, delim in char) return int as
val int;
begin
  val := length(replace(str, delim, delim || ' '));
  return val - length(str); 
end;

create type token_list is varray(100) of varchar2(200);

CREATE or replace function tokenize (str varchar2, delim char) return token_list as
ret token_list;
target int;
i int;
this_delim int;
last_delim int;
BEGIN
  ret := token_list();
  i := 1;
  last_delim := 0;
  target := splitter_count(str, delim);
  while i <= target
  loop
    ret.extend();
    this_delim := instr(str, delim, 1, i);
    ret(i):= substr(str, last_delim + 1, this_delim - last_delim -1);
    i := i + 1;
    last_delim := this_delim;
  end loop;
  ret.extend();
  ret(i):= substr(str, last_delim + 1);
  return ret;
end;

Вы можете использовать его так:

select tokenize('hi you person', ' ') from dual;
VARCHAR(hi,you,person)
8
ответ дан 5 December 2019 в 05:34
поделиться

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

Если вы используете параметры, чтобы разделить строку номеров csv (например: 1,2,3,4), используйте это в инструкции IN посмотрите на функцию str2tbl () в вопросе 670922 . Сделав несколько изменений, вы можете изменить его на VARCHAR2 или что угодно, что вам нужно.

В следующем примере вы можете установить : sMyCatagories равным '1,2,3 , 4 '

create or replace type myTableType as table of number;

create or replace function str2tbl( p_str in varchar2 ) return myTableType
  as
     l_str   long default p_str || ',';
     l_n        number;
     l_data    myTableType := myTabletype();
  begin
      loop
          l_n := instr( l_str, ',' );
          exit when (nvl(l_n,0) = 0);
          l_data.extend;
          l_data( l_data.count ) := ltrim(rtrim(substr(l_str,1,l_n-1)));
          l_str := substr( l_str, l_n+1 );
      end loop;
      return l_data;
  end;

и использование его в операторе select ....

SELECT 
  *
FROM
  atable a 
WHERE 
  a.category in (
        select * from INLIST (
           select cast(str2tbl(:sMyCatagories) as mytableType) from dual
        ) 
  );

Это действительно полезно, только если вы используете параметры. Если вы совместно используете SQL в своем приложении, просто используйте обычный оператор IN.

SELECT 
  *
FROM
  atable a 
WHERE 
  a.category in (1,2,3,4);
2
ответ дан 5 December 2019 в 05:34
поделиться

Джойс,

Вот три примера:

1) Использование dbms_utility.comma_to_table. Это не универсальная подпрограмма, потому что элементы должны быть действительными идентификаторами. С помощью некоторых грязных уловок мы можем сделать его более универсальным:

SQL> declare
  2    cn_non_occuring_prefix constant varchar2(4) := 'zzzz';
  3    mystring varchar2(2000):='a:sd:dfg:31456:dasd: :sdfsdf'; -- just an example
  4    l_tablen binary_integer;
  5    l_tab    dbms_utility.uncl_array;
  6  begin
  7    dbms_utility.comma_to_table
  8    ( list   => cn_non_occuring_prefix || replace(mystring,':',','||cn_non_occuring_prefix)
  9    , tablen => l_tablen
 10    , tab    => l_tab
 11    );
 12    for i in 1..l_tablen
 13    loop
 14      dbms_output.put_line(substr(l_tab(i),1+length(cn_non_occuring_prefix)));
 15    end loop;
 16  end;
 17  /
a
sd
dfg
31456
dasd

sdfsdf

PL/SQL-procedure is geslaagd.

2) Использование SQL-соединения по уровням. Если у вас 10g или выше, вы можете использовать подход подключения по уровням в сочетании с регулярными выражениями, например:

SQL> declare
  2    mystring varchar2(2000):='a:sd:dfg:31456:dasd: :sdfsdf'; -- just an example
  3  begin
  4    for r in
  5    ( select regexp_substr(mystring,'[^:]+',1,level) element
  6        from dual
  7     connect by level <= length(regexp_replace(mystring,'[^:]+')) + 1
  8    )
  9    loop
 10      dbms_output.put_line(r.element);
 11    end loop;
 12  end;
 13  /
a
sd
dfg
31456
dasd

sdfsdf

PL/SQL-procedure is geslaagd.

3) Снова используя SQL-соединение по уровням, но теперь в сочетании со старым добрым SUBSTR / INSTR на случай вы используете версию 9, как и вы:

    SQL> declare
      2    mystring varchar2(2000):='a:sd:dfg:31456:dasd: :sdfsdf'; -- just an example
      3  begin
      4    for r in
      5    ( select substr
      6             ( str
      7             , instr(str,':',1,level) + 1
      8             , instr(str,':',1,level+1) - instr(str,':',1,level) - 1
      9             ) element
     10        from (select ':' || mystring || ':' str from dual)
     11     connect by level <= length(str) - length(replace(str,':')) - 1
     12    )
     13    loop
     14      dbms_output.put_line(r.element);
     15    end loop;
     16  end;
     17  /
    a
    sd
    dfg
    31456
    dasd

    sdfsdf

PL/SQL-procedure is geslaagd.

Вы можете увидеть еще несколько подобных техник в этом блоге: http://rwijk.blogspot.com/2007/11/interval-based-row-generation .html

Надеюсь, это поможет.

С уважением, Роб.


В ответ на ваш комментарий:

Пример вставки разделенных значений в нормализованную таблицу.

Сначала создайте таблицы:

SQL> create table csv_table (col)
  2  as
  3  select 'a,sd,dfg,31456,dasd,,sdfsdf' from dual union all
  4  select 'a,bb,ccc,dddd' from dual union all
  5  select 'zz,yy,' from dual
  6  /

Table created.

SQL> create table normalized_table (value varchar2(10))
  2  /

Table created.

Поскольку вас интересует подход dbms_utility.comma_to_table, я упоминаю его Вот. Однако я определенно не рекомендую этот вариант из-за особенностей идентификатора и из-за медленной обработки строки за строкой.

SQL> declare
  2    cn_non_occuring_prefix constant varchar2(4) := 'zzzz';
  3    l_tablen binary_integer;
  4    l_tab    dbms_utility.uncl_array;
  5  begin
  6    for r in (select col from csv_table)
  7    loop
  8      dbms_utility.comma_to_table
  9      ( list   => cn_non_occuring_prefix || replace(r.col,',',','||cn_non_occuring_prefix)
 10      , tablen => l_tablen
 11      , tab    => l_tab
 12      );
 13      forall i in 1..l_tablen
 14        insert into normalized_table (value)
 15        values (substr(l_tab(i),length(cn_non_occuring_prefix)+1))
 16      ;
 17    end loop;
 18  end;
 19  /

PL/SQL procedure successfully completed.

SQL> select * from normalized_table
  2  /

VALUE
----------
a
sd
dfg
31456
dasd

sdfsdf
a
bb
ccc
dddd
zz
yy


14 rows selected.

Я действительно рекомендую этот единственный вариант SQL:

SQL> truncate table normalized_table
  2  /

Table truncated.

SQL> insert into normalized_table (value)
  2   select substr
  3          ( col
  4          , instr(col,',',1,l) + 1
  5          , instr(col,',',1,l+1) - instr(col,',',1,l) - 1
  6          )
  7     from ( select ',' || col || ',' col from csv_table )
  8        , ( select level l from dual connect by level <= 100 )
  9    where l <= length(col) - length(replace(col,',')) - 1
 10  /

14 rows created.

SQL> select * from normalized_table
  2  /

VALUE
----------
a
a
zz
sd
bb
yy
dfg
ccc

31456
dddd
dasd

sdfsdf

14 rows selected.

С уважением, Роб.

16
ответ дан 5 December 2019 в 05:34
поделиться

Похоже, вы не хотите добавлять схему (типы, функции). Единственный способ SQL для синтаксического анализа текста с разделителями - это «сойти с ума» с помощью вызовов instr и substr.

    DECLARE
      V_CSV_STRING VARCHAR2(100);
    BEGIN
      --Create a test delimited list of first_name, last_name, middle_init
      V_CSV_STRING := 'Brian,Hart,M';

    select substr( V_CSV_STRING||',', 1, instr(V_CSV_STRING,',')-1 ) FIRST_NAME,
           substr( V_CSV_STRING||',,', instr( V_CSV_STRING||',,', ',') +1, 
                             instr( V_CSV_STRING||',,', ',', 1, 2 )-instr(V_CSV_STRING||',,',',')-1 ) LAST_NAME,
           rtrim(substr( V_CSV_STRING||',,', instr( V_CSV_STRING||',,',',',1,2)+1),',') MIDDLE_INIT
     from dual;
     END;

Если вы хотите формализовать структуру и добавить соответствующий код приложения (функции, представления, типы и т. Д.), Я бы посмотрел на Тома Кайта по этой теме ].

2
ответ дан 5 December 2019 в 05:34
поделиться

Я использовал это в конце

create or replace function split
(
   p_list varchar2

) return sys.dbms_debug_vc2coll pipelined
is
   l_idx    pls_integer;
   l_list    varchar2(32767) := p_list;
   l_value    varchar2(32767);
begin
   loop
       l_idx := instr(l_list,',');
       if l_idx > 0 then
           pipe row(substr(l_list,1,l_idx-1));
           l_list := substr(l_list,l_idx+length(','));

       else
           pipe row(l_list);
           exit;
       end if;
   end loop;
   return;
end split;

declare
CURSOR c IS  select occurrence_num, graphics from supp where graphics is not null and graphics not like ' %';
begin
  FOR r IN c LOOP   
      insert into image (photo_id,report_id, filename) 
      select image_key_seq.nextval   photo_id, r.occurrence_num report_id, 
      t.column_value  filename from table(split(cast(r.graphics as varchar2(1000)))) t where t.column_value is not null;
   END LOOP;  
end ;
2
ответ дан 5 December 2019 в 05:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: