سؤال

أريد أن أكون قادرا على تقسيم سلاسل CSV في Oracle 9i

لقد قرأت المقالة التاليةhttp://www.oappssurd.com/2009/03/String-Split-in-oracle.html.

لكنني لم أفهم كيفية جعل هذا العمل. فيما يلي بعض أسئلتي المتعلقة به

  1. هل هذا العمل في Oracle 9i، إن لم يكن، لماذا لا؟
  2. هل هناك طريقة أفضل للذهاب إلى تقسيم سلاسل CSV، ثم الحل المقدم أعلاه؟
  3. هل أحتاج إلى إنشاء نوع جديد؟ إذا كان الأمر كذلك، هل أحتاج إلى امتيازات محددة لذلك؟
  4. هل يمكنني أن أعلن نوع ث / في الوظيفة؟
هل كانت مفيدة؟

المحلول

إليك مزامنة سلسلة ل 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)

نصائح أخرى

جويس،

فيما يلي ثلاثة أمثلة:

1) استخدام dbms_utility.comma_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.

يمكنك رؤية بعض التقنيات مثل هذه، في هذا Blogpost: 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_UTIONTIONMMA_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.

التحيات، روب.

قد ترغب في أن تكون أكثر وضوحا قليلا حول ما تريد القيام به، ثم يمكننا أن نقدم لك إجابة محددة. عرض بعض الكود الخاص بك هو دائما مفيدة :)

إذا كنت تستخدم معلمات، لتقسيم سلسلة من أرقام 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 
  *
FROM
  atable a 
WHERE 
  a.category in (
        select * from INLIST (
           select cast(str2tbl(:sMyCatagories) as mytableType) from dual
        ) 
  );

هذا مفيد حقا فقط إذا كنت تستخدم المعلمات. إذا كنت تقوم بتشغيل SQL في التطبيق الخاص بك، فما عليك سوى استخدام العبارة العادية.

SELECT 
  *
FROM
  atable a 
WHERE 
  a.category in (1,2,3,4);

اعتدت هذا في النهاية

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 ;

يبدو أنك لا تريد إضافة مخطط (أنواع، وظيفة). يتمثل Way 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;

إذا كنت تبحث لإضفاء الطابع الرسمي على هيكل وإضافة رمز التطبيق المناسب (الوظائف أو المشاهدات أو الأنواع وغيرها ...) سألقي نظرة على توم كيت جاري الكتابة على هذا موضوعات.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top