如何在 ORACLE (Varray&loop) 中计算发票的月平均价格
Posted
技术标签:
【中文标题】如何在 ORACLE (Varray&loop) 中计算发票的月平均价格【英文标题】:How can compute monthly average price of an invoice in ORACLE (Varray&loop) 【发布时间】:2017-03-06 07:38:06 【问题描述】:我有一张发票表格,里面有数百万条数据。在我的表格中,有发票以及客户的第一个和最后一个日期。我的目标是计算发票的每月平均价格。例如我有:
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
的发票从 2017 年 1 月 1 日到 2017 年 1 月 17 日有 16 天(不包括 1 月 17 日)。发票价格为 32 美元。因此一天的平均价格是 32/16 = 2$。第二张发票是1b
,从 2017 年 1 月 17 日到 2017 年 2 月 10 日有 24 天。因此平均每天消费 3 美元。并且此发票一月份的部分为 15 天(从 1 月 17 日至 1 月 31 日,包括 1 月 31 日)。总而言之,1 月份的平均消费:16x2$+15x3$=77$。
我认为,我必须使用 varray 来存储月份的数据,并且我必须使用循环来查找 FIRST_DATE
和 LAST_DATE
之间的日期。然而我做不到。还是有其他方式?
【问题讨论】:
你是怎么得到 77 美元的。? .什么意思(=16.2+15.3)?你的问题不清楚.. 抱歉。第一张 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 美元。并且此发票一月份的部分为 15 天(从 1 月 17 日至 1 月 31 日,包括 1 月 31 日)。总而言之,1 月份的平均消费:16.2 美元+15.3 美元=77 美元。现在清楚了吗?如果没有,我可以解释更多.. 谢谢你的帮助 【参考方案1】: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
【讨论】:
谢谢。这段代码很棒:)我要求理解。我无法理解部分: 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 @Abdullah 是递归子查询因式分解子句(也称为递归CTE)。顶部SELECT ... FROM your_table
为原始表中的每一行生成第一个月的数据,然后第二部分UNION ALL SELECT ... FROM month_invoices
递归生成原始表中每一行的剩余月份。最后一位 SELECT ... FROM month_invoices GROUP BY ...
然后将每个月的总和。
感谢您的帮助。这对我来说是很好的信息。再次感谢您【参考方案2】:
这就是我想出的。 内部查询为每张发票的每一天创建一行。 外部查询对它们进行了总结。 它假定发票最长只有 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');
【讨论】:
以上是关于如何在 ORACLE (Varray&loop) 中计算发票的月平均价格的主要内容,如果未能解决你的问题,请参考以下文章
Oracle PL/SQL:如何从 VARRAY 的 REF 中进行 DEREF?
如何在 Oracle SQL Developer 的存储过程中将表名列表作为参数传递?如何使用 PLSQL VARRAY 或嵌套表?