0

関数 (ビュー内で使用) があり、最終結果はカンマ区切りの値のテーブル内の行からの一意の値のリストです。

基本的に、次の表が与えられます。

studentid    classes
12345        MATH 1301, HIST 1301, POLS 1301
57495        MATH 2309, HIST 1301
39485        MATH 1301, HIST 1301

見たい

MATH 1301
MATH 2309
HIST 1301
POLS 1301

ソーステーブルが小さい場合、以下のコードは完璧に機能しますが、30,000 行のテーブルを見ると、次のエラーが発生します。ORA-06532: Subscript outside of limit

私の問題は、コレクションが重複した値を取得しているため、コレクションが大きくなりすぎていることだと確信しています。重複する値自体が問題になるのは、コレクションが大きくなりすぎた場合のみです。重複した値をコレクションから除外するにはどうすればよいですか?

試してみましたが、これは正しいchildnames.exists(element)インデックス値に要素が存在するかどうかを確認するためだけに機能すると思いますか? elementを見てきましたmember ofが、実装方法がわかりません。コレクション要素が存在するかどうかを確認する簡単な方法はありますか? それとも、単純なものを見過ごしていますか?それ以外odcivarchar2listに、より大きなコレクションを許可する別のタイプはありますか?

CREATE OR REPLACE FUNCTION unique_values_from_csv ( p_del VARCHAR2 := ',')
   RETURN SYS.odcivarchar2list
IS
   childnames   SYS.odcivarchar2list := sys.odcivarchar2list ();
   tempvar VARCHAR2(255);
    l_idx    PLS_INTEGER;
    l_list2    VARCHAR2(32767) ;
    l_value VARCHAR2(32767);

   CURSOR tablewalker_cur
   IS
      SELECT distinct classes
        FROM studentclasses;

BEGIN
   FOR recordwalker_rec IN tablewalker_cur
   LOOP
   l_list2 := recordwalker_rec.classes;
      LOOP
         l_idx := INSTR (l_list2, p_del);

         IF l_idx > 0
         THEN

            childnames.EXTEND;            
            tempvar := (LTRIM (RTRIM (SUBSTR (l_list2, 1, l_idx - 1))));
            childnames (childnames.COUNT) :=  tempvar;
            l_list2 := SUBSTR (l_list2, l_idx + LENGTH (p_del));
           end if;

            childnames.EXTEND;
            childnames (childnames.COUNT) :=  (LTRIM (RTRIM (l_list2)));
            EXIT;

      END LOOP;
   END LOOP;

   RETURN childnames;
END unique_values_from_csv;
/
4

2 に答える 2

1
select distinct
  regexp_substr(classes, '([^,]+)(,\s*|$)', 1, occ, '', 1) as class
from
  studentclasses,
  (
    select level as occ
    from dual
    connect by level <= (
      select max(regexp_count(classes, ','))+1 
      from studentclasses
  )
)
where
  regexp_substr(classes, '([^,]+)(,\s*|$)', 1, occ, '', 1) is not null
order by 1
于 2013-03-06T07:01:03.213 に答える
1
create or replace function unique_values_from_csv(p_del varchar2 := ',')
return sys.dbms_debug_vc2coll is
    childnames sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll();
    l_idx    pls_integer;
    l_list2    varchar2(32767) ;

    procedure add_if_not_member(new_element varchar2) is
    begin
        if new_element not member of childnames then
            childnames.extend;
            childnames(childnames.count) := new_element;
        end if;
    end;
begin
    for recordwalker_rec in (select distinct classes from studentclasses)
    loop
        l_list2 := recordwalker_rec.classes;
        loop
            l_idx := instr (l_list2, p_del);
            if l_idx > 0 then
                add_if_not_member(trim(substr (l_list2, 1, l_idx - 1)));
                l_list2 := substr(l_list2, l_idx + length(p_del));
            else
                add_if_not_member(trim(l_list2));
            exit;
            end if;
        end loop;
    end loop;
    return childnames;
end unique_values_from_csv;
/
  • SYS.DBMS_DEBUG_VC2COLL を使用しました。これは であり、TABLE OF VARCHAR2(1000)任意の数の要素をサポートする必要があります。l_list2 varchar2(32767)結果を制限しますが。
  • MEMBER OFが正しい状態です。
  • ELSE を追加 - 元の関数はリストを 2 つに分割するだけでした。
  • カーソルを削除しました - このような小さなクエリの場合、別のレベルの間接化は価値がありません。
  • LTRIM(RTRIM()) の代わりに TRIM() を使用
  • 最善の解決策は、この関数を破棄し、非アトミック データをデータベースに格納するのをやめることです。

関数を使用したサンプル データとクエリを次に示します。

create table studentclasses(studentid number, classes varchar2(4000));

insert into studentclasses
select  12345, 'MATH 1301,HIST 1301,POLS 1301' from dual union all
select  57495, 'MATH 2309,HIST 1301' from dual union all
select  39485, 'MATH 1301,HIST 1301' from dual;
commit;

select unique_values_from_csv from dual;

COLUMN_VALUE
MATH 1301
HIST 1301
POLS 1301
MATH 2309
于 2013-03-06T06:38:29.930 に答える