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 - 原来的功能只是将列表一分为二。
  • 删除了游标——对于这么小的查询,另一个级别的间接是不值得的。
  • 使用 TRIM() 而不是 LTRIM(RTRIM())
  • 最好的解决方案是丢弃这个函数并停止在数据库中存储非原子数据。

这是一些示例数据和使用该函数的查询:

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 回答