2011-03-08 13 views
8

Suponga que tiene una tabla de la siguiente manera:¿Existe una alternativa al operador MULTISET que evite las subconsultas?

CREATE TABLE EMPLOYEE_SALES 
(
    EMPLOYEE_ID NUMBER, 
    PRODUCT_ID NUMBER, 
    SALE_AMOUNT NUMBER 
); 

Y supongamos que está poblada de la siguiente manera:

 
+-------------+------------+-------------+ 
| EMPLOYEE_ID | PRODUCT_ID | SALE_AMOUNT | 
+-------------+------------+-------------+ 
|  1  |  100 |  1.05 | 
|  1  |  200 |  45.67 | 
|  2  |  100 |  3.25 | 
|  2  |  200 |  34.29 | 
+-------------+------------+-------------+ 

Ahora, supongamos que crea un tipo personalizado denominado SALE_TYPE que representa una tupla (PRODUCT_ID, SALE_AMOUNT):

CREATE TYPE SALE_TYPE IS OBJECT 
(
    PRODUCT_ID NUMBER, 
    SALE_AMOUNT NUMBER 
); 

Y supongamos que también creo un tipo personalizado llamado SALES_TYPE que repr esents un TABLE de SALE_TYPE:

CREATE TYPE SALES_TYPE IS TABLE OF SALE_TYPE; 

Quiero consultar el grupo EMPLOYEE_SALES tabla EMPLOYEE_ID. Para cada EMPLOYEE_ID, deseo crear un SALES_TYPE que contenga las ventas de ese empleado. Según la documentación, me gustaría hacer algo como esto:

SELECT 
    EMPLOYEE_ID, 
    CAST 
    (
    MULTISET 
    (
     SELECT 
     PRODUCT_ID, 
     SALE_AMOUNT 
     FROM 
     EMPLOYEE_SALES inner_employee_sales 
     WHERE 
     inner_employee_sales.employee_id = employee_sales.employee_id 
    ) AS SALES_TYPE 
) AS SALES 
FROM 
    EMPLOYEE_SALES 
GROUP BY 
    EMPLOYEE_ID 

espero que esta consulta para producir algo como esto:

 
+-------------+------------------------------+ 
| EMPLOYEE_ID | SALES      | 
+-------------+------------------------------+ 
|  1  | +------------+-------------+ | 
|    | | PRODUCT_ID | SALE_AMOUNT | | 
|    | +------------+-------------+ | 
|    | | 100  |  1.05 | | 
|    | | 200  |  45.67 | | 
|    | +------------+-------------+ | 
+-------------+------------------------------+ 
|  2  | +------------+-------------+ | 
|    | | PRODUCT_ID | SALE_AMOUNT | | 
|    | +------------+-------------+ | 
|    | | 100  |  3.25 | | 
|    | | 200  |  34.29 | | 
|    | +------------+-------------+ | 
+-------------+------------------------------+ 

¿Hay una manera de lograr los mismos resultados sin emitir un sub consulta (la consulta real en la que estoy trabajando es mucho más complicada que este ejemplo artificial)? Por ejemplo, ¿hay algo como esto:

SELECT 
    EMPLOYEE_ID, 
    CAST 
    (
    COLLECT_ALL_RECORDS_WITHIN_THE_CURRENT_GROUP(
     PRODUCT_ID, 
     SALE_AMOUNT 
    ) 
    AS SALES_TYPE 
) AS SALES 
FROM 
    EMPLOYEE_SALES 
GROUP BY 
    EMPLOYEE_ID 

Respuesta

6

En Oracle 10g, se puede utilizar el operador COLLECT de la siguiente manera:

select employee_id, 
     cast(collect(sale_type(product_id, sale_amount)) as sales_type) 
from employee_sales 
group by employee_id 
+0

+1: ¡Guau, fue tan simple! –

3

Usted debe ser capaz de escribir un custom aggregate function lo largo de las líneas de las funciones de agregación que se crearon para concatenar cadenas antes de 11.2 y la función LISTAGG.

Por ejemplo, puedo crear un objeto EMP_OBJ y una colección de los objetos (EMP_TBL) y escribir una función personalizada agregado que genera una colección EMP_TBL de una sentencia SQL sencilla

Crear los tipos básicos

SQL> create type emp_obj as object (
    2 empno number, 
    3 ename varchar2(100) 
    4 ); 
    5/

Type created. 

SQL> create type emp_tbl 
    2 as 
    3 table of emp_obj; 
    4/

Type created. 

crear el tipo que vamos a utilizar para hacer la agregación

SQL> ed 
Wrote file afiedt.buf 

    1 CREATE OR REPLACE TYPE emp_tbl_agg AS OBJECT 
    2 (
    3 g_emp_coll emp_tbl, 
    4 STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT emp_tbl_agg) 
    5  RETURN NUMBER, 
    6 MEMBER FUNCTION ODCIAggregateIterate(self IN OUT emp_tbl_agg , 
    7           value IN  emp_obj) 
    8  RETURN NUMBER, 
    9 MEMBER FUNCTION ODCIAggregateTerminate(self   IN emp_tbl_agg, 
10           returnValue OUT emp_tbl, 
11           flags  IN NUMBER) 
12  RETURN NUMBER, 
13 MEMBER FUNCTION ODCIAggregateMerge(self IN OUT emp_tbl_agg, 
14          ctx2 IN  emp_tbl_agg) 
15  RETURN NUMBER 
16*); 
17/

Type created. 

SQL> ed 
Wrote file afiedt.buf 

    1 CREATE OR REPLACE TYPE BODY emp_tbl_agg IS 
    2 STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT emp_tbl_agg) 
    3  RETURN NUMBER IS 
    4 BEGIN 
    5  sctx := emp_tbl_agg(NULL); 
    6  sctx.g_emp_coll := new emp_tbl(); 
    7  RETURN ODCIConst.Success; 
    8 END; 
    9 MEMBER FUNCTION ODCIAggregateIterate(self IN OUT emp_tbl_agg, 
10           value IN  emp_obj) 
11  RETURN NUMBER IS 
12 BEGIN 
13  SELF.g_emp_coll.extend(); 
14  SELF.g_emp_coll(self.g_emp_coll.count) := value; 
15  RETURN ODCIConst.Success; 
16 END; 
17 MEMBER FUNCTION ODCIAggregateTerminate(self   IN emp_tbl_agg, 
18           returnValue OUT emp_tbl, 
19           flags  IN NUMBER) 
20  RETURN NUMBER IS 
21 BEGIN 
22  returnValue := self.g_emp_coll; 
23  RETURN ODCIConst.Success; 
24 END; 
25 MEMBER FUNCTION ODCIAggregateMerge(self IN OUT emp_tbl_agg, 
26          ctx2 IN  emp_tbl_agg) 
27  RETURN NUMBER IS 
28 BEGIN 
29  FOR i IN ctx2.g_emp_coll.FIRST .. ctx2.g_emp_coll.LAST 
30  LOOP 
31  self.g_emp_coll.extend(); 
32  self.g_emp_coll(self.g_emp_coll.count) := ctx2.g_emp_coll(i); 
33  END LOOP; 
34  RETURN ODCIConst.Success; 
35 END; 
36* END; 
SQL>/

Type body created. 

declarar la función agregada

SQL> create or replace function emp_agg(p_input emp_obj) 
    2 return emp_tbl 
    3 aggregate using emp_tbl_agg; 
    4/

Function created. 

Y ahora un simple grupo utilizando nuestra función agregada a medida generará una colección EMP_TBL

SQL> select deptno, emp_agg(emp_obj(empno, ename)) 
    2 from emp 
    3 group by deptno; 

    DEPTNO 
---------- 
EMP_AGG(EMP_OBJ(EMPNO,ENAME))(EMPNO, ENAME) 
-------------------------------------------------------------------------------- 

     10 
EMP_TBL(EMP_OBJ(7782, 'CLARK'), EMP_OBJ(7934, 'MILLER'), EMP_OBJ(7839, 'KING')) 

     20 
EMP_TBL(EMP_OBJ(7369, 'smith'), EMP_OBJ(7902, 'FORD'), EMP_OBJ(7876, 'ADAMS'), E 

MP_OBJ(7788, 'SCOTT'), EMP_OBJ(7566, 'JONES')) 

     30 
EMP_TBL(EMP_OBJ(7499, 'ALLEN'), EMP_OBJ(7900, 'SM2'), EMP_OBJ(7844, 'TURNER'), E 


    DEPTNO 
---------- 
EMP_AGG(EMP_OBJ(EMPNO,ENAME))(EMPNO, ENAME) 
-------------------------------------------------------------------------------- 

MP_OBJ(7698, 'BLAKE'), EMP_OBJ(7654, 'MARTIN'), EMP_OBJ(7521, 'WARD')) 
+0

+ 1: ¡Me olvidé de las funciones de agregado personalizadas! Claro, es tremendamente detallado, pero sin duda hace el trabajo. ¡Gracias! –

Cuestiones relacionadas