2011-12-02 10 views
6

Tengo bastante experiencia con las funciones analíticas de Oracle, pero esta me ha dejado perplejo. Me patearé a mí mismo si hay una solución obvia :)Interesante desafío de consulta analítica de Oracle

Tengo una tabla, JOURNAL, que registra Inserciones, Actualizaciones y Eliminaciones en otra tabla.

La tabla para la que es un diario es BOND_PAYMENTS, que representa enlaces entre PAYMENTS y ​​BONDS; almacena la cantidad de dinero (AMOUNT) que se asignó a un bono en particular (identificado por BOND_NUMBER) a partir de un pago en particular (identificado por PAYMENT_ID). Además, registra a qué aspecto del enlace se asignó (BOP_DOMAIN) que podría ser 'BON', 'PET' o algún otro código. La tabla BOND_PAYMENTS tiene una clave sustituta (BOP_ID).

Por lo tanto, mi tabla de diario generalmente tendrá 1 o más registros para cada BOP_ID - en primer lugar, un INSert, seguido quizás por algunos UPDates, seguido quizás por un DELete.

Aquí está la tabla REVISTA:

CREATE TABLE JOURNAL 
(JN_DATE_TIME DATE   NOT NULL, 
    JN_OPERATION VARCHAR2(3) NOT NULL, 
    BOP_ID  NUMBER(9) NOT NULL, 
    PAYMENT_ID NUMBER(9) NOT NULL, 
    BOND_NUMBER VARCHAR2(20) NOT NULL, 
    BOP_DOMAIN VARCHAR2(10) NOT NULL, 
    AMOUNT  NUMBER(14,2) NOT NULL 
); 

Aquí es un poco de datos de ejemplo:

INSERT INTO JOURNAL VALUES (TO_DATE('01/01/2010','DD/MM/YYYY'),'INS',1242043,1003700,'9995/10','BON',1800); 
INSERT INTO JOURNAL VALUES (TO_DATE('03/01/2010','DD/MM/YYYY'),'INS',1242046,1003700,'9998/10','BON',1700); 
INSERT INTO JOURNAL VALUES (TO_DATE('04/01/2010','DD/MM/YYYY'),'INS',1242048,1003700,'9999/10','BON',1800); 
INSERT INTO JOURNAL VALUES (TO_DATE('05/01/2010','DD/MM/YYYY'),'INS',1242052,1003700,'10003/10','BON',1600); 
INSERT INTO JOURNAL VALUES (TO_DATE('08/01/2010','DD/MM/YYYY'),'INS',1242058,1003700,'9998/10','BON',100); 
INSERT INTO JOURNAL VALUES (TO_DATE('09/01/2010','DD/MM/YYYY'),'UPD',1242058,1003700,'9998/10','PET',100); 
INSERT INTO JOURNAL VALUES (TO_DATE('01/01/2010','DD/MM/YYYY'),'INS',2242043,1003701,'8995/10','BON',1800); 
INSERT INTO JOURNAL VALUES (TO_DATE('02/01/2010','DD/MM/YYYY'),'INS',2242046,1003701,'8998/10','BON',1700); 
INSERT INTO JOURNAL VALUES (TO_DATE('03/01/2010','DD/MM/YYYY'),'INS',2242048,1003701,'8999/10','BON',1800); 
INSERT INTO JOURNAL VALUES (TO_DATE('04/01/2010','DD/MM/YYYY'),'INS',2242058,1003701,'8998/10','BON',100); 
INSERT INTO JOURNAL VALUES (TO_DATE('05/01/2010','DD/MM/YYYY'),'UPD',2242046,1003701,'8998/10','BON',1500); 
INSERT INTO JOURNAL VALUES (TO_DATE('06/01/2010','DD/MM/YYYY'),'INS',2242052,1003701,'9003/10','BON',1600); 
INSERT INTO JOURNAL VALUES (TO_DATE('07/01/2010','DD/MM/YYYY'),'UPD',2242058,1003701,'8998/10','PET',200); 

Ahora, tengo que extraer un conjunto completo de datos de esta tabla revista pero en un poco diferente formato. El requisito principal es que no queremos que la tabla de diario registre BOP_DOMAIN nunca más, simplemente no es obligatorio.

Necesito generar un historial del monto total para cada registro de BOND_PAYMENT. No puedo usar la tabla BOND_PAYMENT en sí porque solo muestra el estado más reciente de cada registro. Necesito extraer esta información de la revista.

No puedo tomar simplemente un SUM(amount) over(partition by payment_id, bond_number) porque un BOP_ID individual puede actualizarse varias veces; por lo que en cualquier momento en el tiempo solo se debe usar la última cantidad registrada para ese BOP_ID.

Teniendo en cuenta los datos de las muestras anteriores, aquí es una ilustración lo que cabe esperar para producir:

SELECT jn_date_time, 
     jn_operation, 
     bop_id, 
     payment_id, 
     bond_number, 
     bop_domain, 
     amount, 
     ? as running_total 
FROM JOURNAL 
ORDER BY jn_date_time; 

sample data and expected results

Aquí he reproducido a la izquierda los datos de la muestra, para los dos pagos de muestras . A la derecha, tengo "Running Total", que es el resultado esperado. Junto a él (en rojo) está la lógica de cómo calculó el total acumulado de cada fila.

El "Total acumulado" es una instantánea, en el momento de la entrada del diario, del importe total para esa combinación de PAYMENT_ID y BOND_NUMBER. Recuerde, un BOP_ID particular puede actualizarse varias veces; el monto total debe considerar solo el registro más reciente para ese BOP_ID.

Cualquier solución que funcione será aceptable, pero sospecho que una función analítica (o combinación de funciones analíticas) será la mejor manera de resolver esto.

Respuesta

6

probar este

WITH inner AS 
    (SELECT jn_date_time, 
    jn_operation, 
    bop_id, 
    payment_id, 
    bond_number, 
    bop_domain, 
    amount, 
    amount - coalesce(lag(amount) over (partition by bop_id order by jn_date_time), 0)  
     as delta_bop_amount 
    FROM JOURNAL) 
SELECT inner.*, 
sum(delta_bop_amount) 
    over (partition by payment_id, bond_number order by jn_date_time) as running_total 
FROM inner 
ORDER BY bond_number, payment_id 

Esto devolverá la misma respuesta para sus ejemplos.

Necesita dos pasos: la función analítica de la consulta interna determina cuánto cambia cada registro el total de cada BOP_ID.Un INS es una adición directa, una UPD tiene que restar el valor más reciente y agregar el nuevo.

El segundo pase luego hace un total acumulado por enlace/pago.

Estoy asumiendo que se quería tratar de la fianza/pago como una clave natural para la suma continua, y que puede haber múltiples de BOP_ID para cualquier combinación de bonos/pago.

+0

muy agradable :) veo lo que está haciendo allí. En primer lugar, calcular la cantidad de la cantidad cambiado desde la entrada pertinente anterior (a través de LAG), entonces es sólo una cuestión de calcular una suma continua a través de los deltas. –

+0

sus suposiciones son correctas. –

0
SELECT a.*, 
lag(amount,1) over (PARTITION BY bond_number ORDER BY 
payment_id,jn_date_time)recent_amount, 
amount + nvl(lag(amount,1) over (PARTITION BY bond_number ORDER BY 
payment_id,jn_date_time),0) running_total 
FROM JOURNAL a 
ORDER BY payment_id,jn_date_time 

Esta solución proporciona la respuesta exacta que está esperando para la pregunta anterior y también en una sola pasada de tabla :).

Acabo de utilizar una función analítica de latencia para obtener el valor más reciente de la cantidad por combinación de bond_number/payment_id y luego agregué ese valor reciente a la cantidad para obtener el total acumulado ... SIMPLE !!! .. aint it :)

+0

Buen intento, pero se informa incorrectamente de $ 200 para bop_id = 1242058 para una fianza 9998/10 - debe mostrar $ 1800s. La razón es el total acumulado es simplemente agregar la cantidad de la fila anterior ordenados por payment_id/jn_date_time mientras que debe tener en cuenta todos los cambios en las cantidades de pago que más de la historia. Eche un vistazo a la respuesta de @ wrschneider que da los resultados correctos. –

Cuestiones relacionadas