如何在 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_DATELAST_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 从 varray 更改列数据类型

如何在 Oracle SQL Developer 的存储过程中将表名列表作为参数传递?如何使用 PLSQL VARRAY 或嵌套表?

在 Oracle SQL 中创建和填充 Varray

Oracle:PL/SQL 中查看值是不是存在的最快方法:列表、VARRAY 或临时表

如何在表列中访问 SQL 中的 varray 元素