2012-06-08 20 views
10

En Oracle, la función LISTAGG me permite usarla analíticamente con una cláusula OVER (PARTITION BY column..). Sin embargo, no es compatible con el uso de ventanas con las palabras clave ROWS o RANGE.LISTAGG equivalente con la cláusula windowing

Tengo un conjunto de datos de un registro de tienda (simplificado para la pregunta). Tenga en cuenta que la cantidad de la tabla de registro siempre es 1 - un elemento, una línea de transacción.

TranID TranLine ItemId OrderID Dollars Quantity 
------ -------- ------ ------- ------- -------- 
1  101  23845 23  2.99 1 
1  102  23845 23  2.99 1 
1  103  23845 23  2.99 1 
1  104  23845 23  2.99 1 
1  105  23845 23  2.99 1 

que tienen que "igualar" estos datos a una tabla en un sistema de pedido especial donde los artículos se agrupan por cantidad. Tenga en cuenta que el sistema puede tener la misma ID de artículo en múltiples líneas (los componentes pedidos pueden ser diferentes incluso si el artículo es el mismo).

ItemId OrderID Order Line Dollars Quantity 
------ ------- ---------- ------- -------- 
23845 23  1   8.97 3 
23845 23  2   5.98 2 

La única manera de que pueda coincidir con estos datos es mediante la identificación de la orden, Identificación del producto y cantidad en dólares.

Esencialmente necesito llegar al siguiente resultado.

ItemId OrderID Order Line Dollars Quantity Tran ID Tran Lines 
------ ------- ---------- ------- -------- ------- ---------- 
23845 23  1   8.97 3  1  101;102;103 
23845 23  2   5.98 2  1  104;105 

no me importa especialmente si las líneas tran están ordenados de cualquier manera, lo único que importa es que el partido de los montos en dólares y que no "reutilización" una línea desde el registro en la informática el total en el orden especial. No necesito las líneas de tran en una tabla, esto es para informar y la granularidad nunca vuelve al nivel de línea de transacción de registro.

Mi pensamiento inicial era que puedo hacer esto con funciones analíticas para hacer una "mejor coincidencia" para identificar el primer conjunto de filas que coinciden con la cantidad en dólares y la cantidad en el sistema de pedidos, dándome un conjunto de resultados como :

TranID TranLine ItemId OrderID Dollars Quantity CumDollar CumQty 
------ -------- ------ ------- ------- -------- -------- ------ 
1  101  23845 23  2.99 1  2.99  1 
1  102  23845 23  2.99 1  5.98  2 
1  103  23845 23  2.99 1  8.97  3 
1  104  23845 23  2.99 1  11.96  4 
1  105  23845 23  2.99 1  14.95  5 

Hasta ahora todo bien. Pero entonces me intento agregar LISTAGG a mi consulta:

SELECT tranid, tranline, itemid, orderid, dollars, quantity, 
     SUM(dollars) OVER (partition by tranid, itemid, orderid order by tranline) cumdollar, 
     SUM(quantity) OVER (partition by tranid, itemid, orderid order by tranline) cumqty 
     LISTAGG (tranline) within group (order by tranid, itemid, orderid, tranline) OVER (partition by tranid, itemid, orderid) 
FROM table 

descubro que siempre devuelve un agg completa en lugar de un agg acumulativa:

TranID TranLine ItemId OrderID Dollars Quantity CumDollar CumQty ListAgg 
------ -------- ------ ------- ------- -------- -------- ------ ------- 
1  101  23845 23  2.99 1  2.99  1  101;102;103;104;105 
1  102  23845 23  2.99 1  5.98  2  101;102;103;104;105 
1  103  23845 23  2.99 1  8.97  3  101;102;103;104;105 
1  104  23845 23  2.99 1  11.96  4  101;102;103;104;105 
1  105  23845 23  2.99 1  14.95  5  101;102;103;104;105 

Así que esto no es útil.

Preferiría hacer esto en SQL si fuera posible. Soy consciente de que puedo hacer esto con los cursores & lógica de procedimiento.

¿Hay alguna forma de hacer ventanas con la función analítica LISTAGG, o quizás otra función analítica que pueda soportar esto?

Estoy en 11gR2.

+2

+1 para una pregunta interesante muy bien escrita. – DCookie

Respuesta

7

La única manera que puedo pensar para lograr esto es con una consulta correlacionada:

WITH CTE AS 
( SELECT TranID, 
      TranLine, 
      ItemID, 
      OrderID, 
      Dollars, 
      Quantity, 
      SUM(dollars) OVER (PARTITION BY TranID, ItemID, OrderID ORDER BY TranLine) AS CumDollar, 
      SUM(Quantity) OVER (PARTITION BY TranID, ItemID, OrderID ORDER BY TranLine) AS CumQuantity 
    FROM T 
) 
SELECT TranID, 
     TranLine, 
     ItemID, 
     OrderID, 
     Dollars, 
     Quantity, 
     CumDollar, 
     CumQuantity, 
     ( SELECT LISTAGG(Tranline, ';') WITHIN GROUP(ORDER BY CumQuantity) 
      FROM CTE T2 
      WHERE T1.CumQuantity >= T2.CumQuantity 
      AND  T1.ItemID = T2.ItemID 
      AND  T1.OrderID = T2.OrderID 
      AND  T1.TranID = T2.TranID 
      GROUP BY tranid, itemid, orderid 
     ) AS ListAgg 
FROM CTE T1; 

Me di cuenta que no da el resultado exacto que pedías, pero espero que es suficiente para superar la problema del acumulativo LISTAGG y lo pone en su camino.

He configurado un SQL Fiddle para demostrar la solución.

+0

Gracias. Funciona para el ejemplo inicial que proporcioné, pero cuando agrego transacciones/artículos adicionales, aparece el error 'ORA-01427: la subconsulta de una sola fila devuelve más de una fila'. Creo que puedo modificar tu consulta para hacer lo que estoy buscando. Aquí hay un ejemplo donde arroja el error: http://sqlfiddle.com/#!4/53b4d/1 –

+1

También - SQLFiddle? ¡Fantástico! Yendo en los marcadores con seguridad. –

+0

Todo lo que necesitaba hacer era agregar 'y t1.itemid = t2.itemid y t1.orderid = t2.orderid y t1.tranid = t2.tranid' a la unión en la subconsulta. ¿Puedes actualizar la respuesta y acepto? –

2

En su ejemplo, su tabla de registro de tienda contiene 5 filas y su tabla de sistema de orden especial contiene 2 filas. Su conjunto de resultados esperados contiene las dos filas de su tabla de sistema de orden especial y todas las "tranlíneas" de la tabla de registro de su tienda deben mencionarse en la columna "Línea de Tran".

Esto significa que debe agregar esas 5 filas a 2 filas. Lo que significa que no necesita la función analítica LISTAGG, sino la función agregada LISTAGG.

Su desafío es unir las filas de la tabla de registro de la tienda a la fila derecha en la tabla del sistema de orden especial. Estabas en buen camino calculando la suma corriente de dólares y cantidades. El único paso que falta es definir rangos de dólares y cantidades mediante los cuales puede asignar cada fila de registro de tienda a cada fila de sistema de orden especial.

Aquí hay un ejemplo. En primer lugar definir las tablas:

SQL> create table store_register_table (tranid,tranline,itemid,orderid,dollars,quantity) 
    2 as 
    3 select 1, 101, 23845, 23, 2.99, 1 from dual union all 
    4 select 1, 102, 23845, 23, 2.99, 1 from dual union all 
    5 select 1, 103, 23845, 23, 2.99, 1 from dual union all 
    6 select 1, 104, 23845, 23, 2.99, 1 from dual union all 
    7 select 1, 105, 23845, 23, 2.99, 1 from dual 
    8/

Table created. 

SQL> create table special_order_system_table (itemid,orderid,order_line,dollars,quantity) 
    2 as 
    3 select 23845, 23, 1, 8.97, 3 from dual union all 
    4 select 23845, 23, 2, 5.98, 2 from dual 
    5/

Table created. 

Y la consulta:

SQL> with t as 
    2 (select tranid 
    3   , tranline 
    4   , itemid 
    5   , orderid 
    6   , sum(dollars) over (partition by itemid,orderid order by tranline) running_sum_dollars 
    7   , sum(quantity) over (partition by itemid,orderid order by tranline) running_sum_quantity 
    8  from store_register_table srt 
    9 ) 
10 , t2 as 
11 (select itemid 
12   , orderid 
13   , order_line 
14   , dollars 
15   , quantity 
16   , sum(dollars) over (partition by itemid,orderid order by order_line) running_sum_dollars 
17   , sum(quantity) over (partition by itemid,orderid order by order_line) running_sum_quantity 
18  from special_order_system_table 
19 ) 
20 , t3 as 
21 (select itemid 
22   , orderid 
23   , order_line 
24   , dollars 
25   , quantity 
26   , 1 + lag(running_sum_dollars,1,0) over (partition by itemid,orderid order by order_line) begin_sum_dollars 
27   , running_sum_dollars end_sum_dollars 
28   , 1 + lag(running_sum_quantity,1,0) over (partition by itemid,orderid order by order_line) begin_sum_quantity 
29   , running_sum_quantity end_sum_quantity 
30  from t2 
31 ) 
32 select t3.itemid "ItemID" 
33  , t3.orderid "OrderID" 
34  , t3.order_line "Order Line" 
35  , t3.dollars "Dollars" 
36  , t3.quantity "Quantity" 
37  , t.tranid "Tran ID" 
38  , listagg(t.tranline,';') within group (order by t3.itemid,t3.orderid) "Tran Lines" 
39 from t3 
40   inner join t 
41   on ( t.itemid = t3.itemid 
42    and t.orderid = t3.orderid 
43    and t.running_sum_dollars between t3.begin_sum_dollars and t3.end_sum_dollars 
44    and t.running_sum_quantity between t3.begin_sum_quantity and t3.end_sum_quantity 
45    ) 
46 group by t3.itemid 
47  , t3.orderid 
48  , t3.order_line 
49  , t3.dollars 
50  , t3.quantity 
51  , t.tranid 
52/

    ItemID OrderID Order Line Dollars Quantity Tran ID Tran Lines 
---------- ---------- ---------- ---------- ---------- ---------- -------------------- 
    23845   23   1  8.97   3   1 101;102;103 
    23845   23   2  5.98   2   1 104;105 

2 rows selected. 

Saludos,
Rob.

+0

Gracias! Esta es una forma interesante de hacer esto. Funciona bien para los datos de muestra. Surge un problema cuando la tabla de órdenes tiene dos líneas con los mismos $ montos. Eche un vistazo aquí: http://sqlfiddle.com/#!4/06e72/1. La línea de pedido 2 solo muestra una línea de transición coincidente, aunque debería haber cinco. Voy a jugar con la consulta para ver si puedo hacer que funcione. –

+0

Lo siento, tuve un error tipográfico en mis insertos, los identificadores de los ítems eran diferentes. Sin embargo, sigue siendo un problema si está haciendo coincidir el artículo "restante" 2.99 con el artículo 14.95 en la tabla de pedidos. –

+0

Lo investigaré esta noche. –

Cuestiones relacionadas