Collections 集合中存在oracle值

Collections 集合中存在oracle值,collections,plsql,oracle11g,exists,oracle-type,Collections,Plsql,Oracle11g,Exists,Oracle Type,我在一个视图中使用了一个函数,其最终结果是一个由逗号分隔的值组成的表中的行中的唯一值列表 基本上给出了以下表格: 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 如果源表很小,下面的代码

我在一个视图中使用了一个函数,其最终结果是一个由逗号分隔的值组成的表中的行中的唯一值列表

基本上给出了以下表格:

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
如果源表很小,下面的代码工作得很好,但是当查看一个30000行的表时,我得到以下错误。ORA-06532:下标超出限制

我很确定我的问题是集合太大了,因为它得到了重复的值。只有当集合变得太大时,重复值本身才会成为问题。如何将重复值保留在集合之外

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;
/
我尝试过childnames.existselement,但我相信这只适用于查看索引值元素处是否存在元素,对吗?当然,我已经看过的成员,但我不知道如何实施它。。是否有一种简单的方法来检查集合元素是否存在?还是我看起来太简单了?除了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;
/
使用SYS.DBMS_DEBUG_VC2COLL,它是一个VARCHAR21000表,应该支持任意数量的元素。尽管l_list2 varchar232767将限制结果。 成员的状态是正确的。 添加了一个ELSE-原来的函数只是将列表一分为二。 删除了光标-对于这样一个小的查询,另一个间接级别是不值得的。 使用饰件而不是LTRIMRTRIM 最好的解决方案是扔掉这个函数,停止在数据库中存储非原子数据。 下面是一些示例数据和使用函数的查询:

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

+1可能比我的解决方案更有效。尽管对于大量数据,它的运行速度似乎慢得多。例如,如果您获取我的样本数据,并将表插入自身10次。@jonearles-是的,regexp操作相当占用CPU。
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