2011-12-02 65 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的答案,它給出了正確的結果。 –