2013-03-06 46 views
0

我有一個函數(在視圖中使用),最終結果是逗號分隔值表格中行的唯一值列表。oracle值存在於集合中

本質上給出的表:

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: 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; 
/

回答

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 
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 
+0

+1可能會比我更好的解決方案。雖然對於大量數據,它似乎運行速度要慢得多。例如,如果您抽取我的樣本數據並將表格插入本身10次。 – 2013-03-06 14:49:51

+0

@jonearles - 是的,正則表達式操作相當CPU密集型。 – 2013-03-06 14:53:34