当前位置: 代码迷 >> Oracle认证考试 >> 数据统计难题,该如何处理
  详细解决方案

数据统计难题,该如何处理

热度:7927   发布时间:2013-02-26 00:00:00.0
数据统计难题
已有充值表和消费表,要求统计每个月的卡充值总额和这些卡至今的消费总额。举例如下:
充值表(CZ):
卡号(cardno) 充值次数(times) 充值日期(loddate) 充值金额(lodamt)
91034567 1 2010-2-12 100
91034567 2 2010-5-21 100
91034567 3 2010-9-16 200
91045678 1 2010-3-1 200
91045678 2 2010-3-23 300
91056789 1 2010-2-19 300
。。。。。。
消费表(XF):
卡号(cardno) 面值(blance) 消费金额(amt) 消费日期(date) 
91034567 100 40.00 2010-2-15 
91034567 100 60.00 2010-3-26 
91034567 100 100.00 2010-8-11
91034567 100 160.00 2010-10-3  
91045678 200 120.00 2010-3-1 
91045678 200 80.00 2010-3-5 
91045678 200 220.00 2010-5-15 
91056789 300 278.00 2010-5-2 
。。。。。。。
统计2月份的数据如下:
充值总额(lodall)= 400(100+300 )
消费总额(amtall)= 378(40+60+278)
统计3月份数据
充值总额(lodall)= 500(200+300 )
消费总额(amtall)= 200(120+80)
。。。。。。。
要求格式如下:
LODDATE LODALL AMTALL
------- ---------- ----------
201002 400 378
201003 500 200
。。。。。。
统计每个月的充值总额没有问题,但统计每个月的消费总额就比较困难了。因为卡片可以回收,所以存在一个卡号重复使用(如91034567,第一次2月份充值后全部消费完,第二次5月份充值又使用了),请各位大侠帮助一下,怎么写这个统计语句?

注意三点:
1、不是月度统计,是统计卡片从充值到当前为止的消费,但要考虑回收问题,如果回收后再次充值,则此后的消费计算在下次充值后的消费统计中
2、同一张卡片前后几次充值的金额可以不同
3、卡片的面额等于第一次的充值金额,以后再充值,面额不会变

------解决方案--------------------------------------------------------
SQL code
SQL> SELECT * FROM cz; CARDNO        TIMES LODDATE         LODAMT-------- ---------- ----------- ----------91034567          1 2010-2-12          10091034567          2 2010-5-21          10091034567          3 2010-9-16          20091045678          1 2010-3-1           20091045678          2 2010-3-23          30091056789          1 2010-2-19          300 6 rows selectedSQL> SELECT * FROM xf; CARDNO       BLANCE        AMT PAYDATE-------- ---------- ---------- -----------91034567        100         40 2010-2-1591034567        100         60 2010-3-2691034567        100        100 2010-8-1191034567        100        160 2010-10-391045678        200        120 2010-3-191045678        200         80 2010-3-591045678        200        220 2010-5-1591056789        300        278 2010-5-2 8 rows selectedSQL> SELECT to_char(loddate, 'yyyymm') loddate, SUM(lodamt) lodall, SUM(amt) amtall  2    FROM (SELECT c.*,  3                 (SELECT SUM(amt)  4                    FROM xf x  5                   WHERE c.cardno = x.cardno  6                     AND x.paydate BETWEEN c.loddate AND c.lloddate) amt  7            FROM (SELECT cz.*,  8                         lead(loddate, 1, DATE '3000-01-01')  9                         over (PARTITION BY cardno ORDER BY loddate) - 1 lloddate 10                    FROM cz) c) 11   GROUP BY to_char(loddate, 'yyyymm'); LODDATE     LODALL     AMTALL------- ---------- ----------201002         400        378201003         500        420201005         100        100201009         200        160 SQL>
------解决方案--------------------------------------------------------
SQL code
SELECT TO_CHAR(A.LODDATE,'YYYYMM') LODDATE,MAX(A.SAMT) AS LODAMT,SUM(B.AMT)FROM (SELECT CZ.*,SUM(LODAMT) OVER(PARTITION BY TO_CHAR(LODDATE,'YYYYMM')) SAMTFROM CZ) A,XF BWHERE A.CARDNO = B.CARDNOAND A.LODDATE <= B.PAYDATEAND NOT EXISTS(SELECT 1 FROM CZ CWHERE C.CARDNO= A.CARDNO AND C.LODDATE <= B.PAYDATEAND C.LODDATE > A.LODDATE)GROUP BY TO_CHAR(A.LODDATE,'YYYYMM')
------解决方案--------------------------------------------------------
一个SQL搞定不一定是最好的,
如果使用SQL,关键要看你的SQL调优情况!!
------解决方案--------------------------------------------------------
你的需求复杂,用一条sql搞定,效率不一定高,考虑一下存储过程实现吧
------解决方案--------------------------------------------------------
-- 脑子中不要有这个概念:复杂的问题一个SQL搞定,这就是牛皮啦......!

-- 关键得看效率!如果一个SQL搞定啦,但是老板为了看到结果,等待了12个小时,
  相关解决方案