2017-03-06 62 views
0

我有一个发票表,并有数百万的数据。在我的表格中,有发票和客户的第一个和最后一个日期。我的目标是计算发票的月平均价格。比如我有:如何计算ORACLE中发票的月平均价格(Varray&循环)

CUSTOMER_ID  INVOICE_ID FIRST_DATE  LAST_DATE  AMOUNT_OF_INVOICE 
9876543   1a   1 Jan 2017  17 Jan 2017   32$ 
9876543   1b   17 Jan 2017  10 Feb 2017   72$ 
9876543   1c   10 Feb 2017  7 March 2017  100$ 
9876543   1d   7 March 2017 1 April 2017  25$ 
9870011   2a   1 Jan 2017  10 Jan 2017   18$ 
9870011   2b   10 Jan 2017  10 Feb 2017   62$ 
9870011   2c   10 Feb 2017  1 April 2017  50$ 


my target is: 

CUSTOMER_ID  MONTH   MONTHLY_AVERAGE_PRICE 
9876543   January 2017   77$     (=16x2+15x3) 
9876543   February 2017  103$    (=9x3+19x4) 
9876543   March 2017   49$     (=6x4+25x1) 
9870011   January 2017   62$     (=9x2+22x2) 
9870011   February 2017  37$     (=9x2+19x1) 
9870011   March 2017   31$     (=31x1) 

比如我计算77 $(=16x2+15x3)由: 第一张发票这INVOICE_ID是1a有16天,从2017年1月一日至1月17日2017年(不incuding 1月17日)。而发票的价格是32美元。因此一天的平均价格是32/16 = 2 $。第二张发票是1b,从2017年1月17日至2017年2月10日有24天,因此平均每天消费为3美元。 1月份的发票部分为15天(1月17日至1月31日包括1月31日)。总而言之,1月份平均消费量:16x2 $ + 15x3 $ = 77 $。

在这里,我认为,我必须使用varray来存储数据月,我必须使用循环来找到FIRST_DATELAST_DATE之间的天数。但是我做不到。还是以其他方式存在?

+0

如何获得77 $。? 。和什么意思(= 16.2 + 15.3)?你的问题不清楚.. – scaisEdge

+0

哦,对不起。 INVOICE_ID为1a的第一张发票为2017年1月1日至2017年1月17日期间的16天(不包括1月17日)。而发票的价格是32美元。因此一天的平均价格是32/16 = 2 $。第二张发票为1b,2017年1月17日至2017年2月10日为24天,因此平均每天消费为3美元。 1月份的发票部分为15天(1月17日至1月31日包括1月31日)。总而言之,1月的平均消费:16.2美元+ 15.3美元= 77美元。现在清楚吗?如果没有,我可以解释更多..谢谢你的帮助 – Abdullah

回答

0

Oracle查询

WITH month_invoices (c_id, i_id, first_date, last_date, month_start, month_end, amount) 
AS (
    SELECT customer_id, 
     invoice_id, 
     first_date, 
     last_date, 
     first_date, 
     LEAST(ADD_MONTHS(TRUNC(first_date, 'MM'), 1), last_date), 
     amount_of_invoice 
    FROM your_table 
UNION ALL 
    SELECT c_id, 
     i_id, 
     first_date, 
     last_date, 
     month_end, 
     LEAST(ADD_MONTHS(month_end, 1), last_date), 
     amount 
    FROM month_invoices 
    WHERE month_end < last_date 
) 
SELECT c_id AS customer_id, 
     TRUNC(month_start, 'MM') AS month, 
     SUM(amount * (month_end - month_start)/(last_date - first_date)) 
     AS average_monthly_price 
FROM month_invoices 
GROUP BY c_id, TRUNC(month_start, 'MM') 
ORDER BY customer_id, month; 

输出

CUSTOMER_ID MONTH  AVERAGE_MONTHLY_PRICE 
----------- ---------- --------------------- 
    9876543 2017-01-01     77 
    9876543 2017-02-01     103 
    9876543 2017-03-01     49 
    9870011 2017-01-01     62 
    9870011 2017-02-01     37 
    9870011 2017-03-01     31 
+0

谢谢。这段代码很好:)我要求理解。我无法理解的部分:UNION ALL SELECT C_ID, i_id, FIRST_DATE, LAST_DATE, MONTH_END, LEAST(ADD_MONTHS(MONTH_END,1),LAST_DATE) 量 FROM month_invoices WHERE MONTH_END Abdullah

+0

@Abdullah它是一个递归的子查询分解子句(也称为递归CTE)。顶部部分'SELECT ... FROM your_table'生成原始表中每行的第一个月的数据,然后第二部分'UNION ALL SELECT ... FROM month_invoices'递归以生成每行的剩余月份原来的桌子。最后一位'SELECT ... FROM month_invoices GROUP BY ...'然后每个月总计一次。 – MT0

+0

谢谢你的帮助。这对我来说是很好的信息。再次感谢你 – Abdullah

0

这是我想出了。
内部查询为每张发票的每一天创建一行。
外部查询将它们汇总起来。
它假设发票最长只有999天。

select customer_id, month, sum(average_cost_per_day) average_cost_per_day 
from (
    select max(customer_id) customer_id, 
     invoice_id, 
     to_char(first_date + n-1, 'MONTH YYYY') month, 
     count(1)*max(amount_of_invoice)/max(last_date-first_date) average_cost_per_day 
    from your_table 
    inner join (
    select level n 
    from dual 
    connect by level <= 1000 
) 
    on (first_date + n-1 < last_date) 
    group by invoice_id, to_char(first_date + n-1, 'MONTH YYYY') 
) 
group by customer_id, month 
order by customer_id desc, to_date(month,'MONTH YYYY');