2011-12-02 59 views
6

我对Oracle分析功能有相当丰富的经验,但是这个难题让我难堪。我会踢自己,如果有明显的解决方案:)有趣的Oracle分析查询挑战

我有一个表,JOURNAL,其中记录插入,更新和删除在另一个表上。

它是期刊的表是BOND_PAYMENTS,它表示支付和债券之间的链接;它存储从特定付款(由PAYMENT_ID标识)分配给特定债券(由BOND_NUMBER标识)的金额(AMOUNT)。另外,它还记录了分配给它的债券的哪些方面(BOP_DOMAIN),可能是'BON','PET'或其他代码。 BOND_PAYMENTS表具有代理键(BOP_ID)。

因此,我的日志表通常会为每个BOP_ID有一个或多个记录 - 首先,INSert,后面也许是一些UPDATE,后面跟着一个DELete。

下面是作者表:

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 
); 

下面是一些样本数据:

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); 

现在,我需要从这个期刊表,但略有不同的提取全套资料格式。主要的要求是我们不希望日记表再次记录BOP_DOMAIN - 它不是必需的。

我需要为每个BOND_PAYMENT记录生成总金额的历史记录。我不能使用BOND_PAYMENT表本身,因为它只显示每个记录的最新状态。我需要从杂志上挖掘这些信息。

我不能只采取SUM(amount) over(partition by payment_id, bond_number),因为单个BOP_ID可能会更新多次;所以在任何时候只能使用为该BOP_ID记录的最新数量。

鉴于上述样本数据,这里是我期望产生一个例证:

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

在这里,我复制左侧的样本数据,有两个样品费。在右边我有“Running Total”,这是预期的输出。在它旁边(红色)是逻辑如何计算每行的运行总数。

“正在运行的总计”是在日记帐分录时间点的快照,该快照在PAYMENT_ID和BOND_NUMBER的组合总量中为快照。请记住,一个特定的BOP_ID可能会更新多次;总金额必须只考虑该BOP_ID的最新记录。

任何有效的解决方案都可以接受,但我怀疑解析函数(或分析函数的组合)将是解决此问题的最佳方法。

回答

6

试试这个

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 

这将返回相同的答案你的例子。

您需要两遍 - 内部查询中的分析函数会计算出每条记录会为每个BOP_ID更改总数。INS是一个直接的补充,UPD必须减去最近的值并添加新的值。

第二次通过债券/支付进行总计。

我假设您想将债券/付款作为运行金额的自然关键字,并且任何债券/付款组合可能有多个BOP_ID。

+0

非常好:)我看到你在那里做什么。你首先计算从前一个相关条目(通过LAG)改变的金额数量,然后计算跨越delta的运行总和。 –

+0

你的假设是正确的。 –

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 

该解决方案提供了您对上述问题的准确答案,以及在单个表格中传递:)。

我刚刚使用了滞后分析函数来获取每个bond_number/payment_id组合的金额的最近值,然后将该近期金额值添加到金额以获得运行总额... SIMPLE !!! .. aint它:)

+0

不错的尝试,但它错误地报告$ 200为bop_id = 1242058债券9998/10 - 它应该显示$ 1800。原因是您的运行总额仅仅是添加上一行按payment_id/jn_date_time排序的金额,而它需要考虑历史记录中所有支付金额的更改。看看@ wrschneider的答案,它给出了正确的结果。 –