Posts Tagged ‘oggetti’

Aggregazione di stringhe, funzioni pipelined ed oggetti

14 febbraio 2010

Volevo ripartire dal post precedente per mostrare come una table function può essere utilizzata per risolvere il problema della string aggregation che ho già affrontato in un altro articolo.

Cominciamo con un esempio semplice e poi cerchiamo di generalizzarlo.
Vogliamo aggregare i nomi dei dipendenti (ENAME) per dipartimento (DEPTNO):


SQL> create or replace type t_strings is table of varchar2(100);
  2  /

Type created.

SQL>
SQL> create or replace type t_dept is object (deptno number, emps t_strings);
  2  /

Type created.

SQL>
SQL> create or replace type t_depts is table of t_dept;
  2  /

Type created.

SQL>
SQL> create or replace function dept_aggr
  2  return t_depts PIPELINED is
  3  s t_strings;
  4  dr t_dept;
  5  begin
  6    for r in (select * from dept) loop
  7       select ename
  8         bulk collect into s
  9         from emp
 10        where deptno=r.deptno;
 11       dr := t_dept(r.deptno,s);
 12       pipe row (dr);
 13    end loop;
 14    return;
 15  end;
 16  /

Function created.

SQL> select * from table(dept_aggr);

    DEPTNO EMPS
---------- -------------------------------------------------------------------------
        10 T_STRINGS('CLARK', 'KING', 'MILLER')
        20 T_STRINGS('SMITH', 'JONES', 'SCOTT', 'ADAMS', 'FORD')
        30 T_STRINGS('ALLEN', 'WARD', 'MARTIN', 'BLAKE', 'TURNER', 'JAMES')
        40 T_STRINGS()

La funzione non fa altro che aggegare i nomi in una collezione e restituire un record formato da codice dipartimento e collezione dei nomi dei dipendenti.
Cerchiamo adesso di generalizzare:

Innanzi tutto droppiamo tutto per ricominciare daccapo:

drop function dept_aggr;
drop type t_depts;
drop type t_dept;
drop type t_strings;

Poi creiamo dei tipi un po’ più generici:


SQL> create or replace type t_strings is table of varchar2(100);
  2  /

Type created.

SQL> create or replace type t_stragg is object
  2  (strings t_strings,
  3   MEMBER FUNCTION tostring(sep varchar2 default ',') RETURN varchar2,
  4   MEMBER FUNCTION contains(val varchar2) RETURN pls_integer
  5  ) NOT FINAL;
  6  /

Type created.

SQL> create or replace type body t_stragg is
  2   MEMBER FUNCTION tostring(sep varchar2 default ',') RETURN varchar2  IS
  3    retstr varchar2(32000);
  4    BEGIN
  5      if strings.count>0 then
  6        for i in strings.first..strings.last loop
  7          retstr := retstr||strings(i)||sep;
  8        end loop;
  9      end if;
 10      RETURN rtrim(retstr,sep);
 11    END;
 12   MEMBER FUNCTION contains(val varchar2) RETURN pls_integer is
 13    BEGIN
 14      if strings.count>0 then
 15        for i in strings.first..strings.last loop
 16          if strings(i) = val then
 17             return i;
 18          end if;
 19        end loop;
 20      end if;
 21      return 0;
 22    END;
 23  end;
 24  /

Type body created.

Questi tipi possono essere utilizzati per qualunque problema di aggregazione di stringhe ed offrono un paio di funzionalità in più che utilizzeremo più avanti.

Creiamo gli oggetti specifici di questo problema estendendo quello generico:


SQL> create or replace type t_dept under t_stragg
  2  (deptno number);
  3  /

Type created.

SQL>
SQL> create or replace type t_depts is table of t_dept;
  2  /

Type created.

La parola chiave UNDER consente di creare un oggetto come estensione di un altro creato prima.
Attenzione: l’oggetto che si intende estendere deve essere stato dichiarato NOT FINAL.

La funzione resta praticamnete identica alla precedente, a parte il fatto che nel tipo T_DEPT è cambiato l’ordine delle proprietà:


SQL> create or replace function dept_aggr
  2  return t_depts PIPELINED is
  3  s t_strings;
  4  dr t_dept;
  5  begin
  6    for r in (select * from dept) loop
  7       select ename
  8         bulk collect into s
  9         from emp
 10        where deptno=r.deptno;
 11       dr := t_dept(s,r.deptno);
 12       pipe row (dr);
 13    end loop;
 14    return;
 15  end;
 16  /

Function created.

Rispetto a prima abbiamo maggiore flessibilità perché possiamo:

Facilmente ottenere l’aggregazione di stringhe:


SQL> select deptno, r.tostring() names
  2  from table(dept_aggr) r;

    DEPTNO NAMES
---------- ----------------------------------------------
        10 CLARK,KING,MILLER
        20 SMITH,JONES,SCOTT,ADAMS,FORD
        30 ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES
        40

Utilizzando qualunque separatore:


SQL> select deptno, r.tostring('#') names
  2  from table(dept_aggr) r
  3  ;

    DEPTNO NAMES
---------- --------------------------------------------
        10 CLARK#KING#MILLER
        20 SMITH#JONES#SCOTT#ADAMS#FORD
        30 ALLEN#WARD#MARTIN#BLAKE#TURNER#JAMES
        40

SQL> select deptno, r.tostring(' ') names
  2  from table(dept_aggr) r;

    DEPTNO NAMES
---------- --------------------------------------------
        10 CLARK KING MILLER
        20 SMITH JONES SCOTT ADAMS FORD
        30 ALLEN WARD MARTIN BLAKE TURNER JAMES
        40

Facilmente ricercare nelle stringhe aggregate:


SQL> select deptno, r.tostring() names
  2  from table(dept_aggr) r
  3  where r.contains('JONES')>0;

    DEPTNO NAMES
---------- ---------------------------------------
        20 SMITH,JONES,SCOTT,ADAMS,FORD

Possiamo comunque ancora estrarre i nomi dei dipendenti sotto forma di oggetto:


SQL> select deptno, strings
  2  from table(dept_aggr) r;

    DEPTNO STRINGS
---------- --------------------------------------------------------------------
        10 T_STRINGS('CLARK', 'KING', 'MILLER')
        20 T_STRINGS('SMITH', 'JONES', 'SCOTT', 'ADAMS', 'FORD')
        30 T_STRINGS('ALLEN', 'WARD', 'MARTIN', 'BLAKE', 'TURNER', 'JAMES')
        40 T_STRINGS()
        

Oppure il recordset originale disaggregato:


SQL> select deptno, column_value name
  2* from table(dept_aggr) r, table(r.strings)

    DEPTNO NAME
---------- -----------------------------------------
        10 CLARK
        10 KING
        10 MILLER
        20 SMITH
        20 JONES
        20 SCOTT
        20 ADAMS
        20 FORD
        30 ALLEN
        30 WARD
        30 MARTIN
        30 BLAKE
        30 TURNER
        30 JAMES

14 rows selected.

Alla prossima,
Massimo