Sql Oracle:每月分解多个条目

Sql Oracle:每月分解多个条目,sql,oracle,date-arithmetic,Sql,Oracle,Date Arithmetic,我的数据如下表所示 start_date end_date amount 15-01-2012 25-01-2012 100 26-01-2012 10-02-2012 100 11-02-2012 29-02-2012 100 我想把这些条目分解如下 start_date end_date amount 15-01-2012 25-01-2012 100 26-01

我的数据如下表所示

start_date      end_date        amount
15-01-2012      25-01-2012      100
26-01-2012      10-02-2012      100
11-02-2012      29-02-2012      100
我想把这些条目分解如下

start_date      end_date        amount
15-01-2012      25-01-2012      100
26-01-2012      10-02-2012      100
11-02-2012      29-02-2012      100
15-01至25-01=100 26-01至31-01=100/10-02-2012-26-01-2012+1*31-01-2012-26-01-2012+1=37.5 因此,在2012年1月15日至2012年1月31日期间,平均值为100+37.5=137.5 2月份的金额应为

start_date      end_date        amount
15-01-2012   31-01-2012                  137.5
01-02-2012   29-02-2012          162.5
01-02至10-02=100/10-02-2012-26-01-2012+1*01-02-2012-10-02-2012+1=62.5 11-02至29-02=100 因此,对于2012年2月1日至2012年2月29日的数据,其=62.5+100=162.5

所以最终的输出应该是

start_date      end_date        amount
15-01-2012   31-01-2012                  137.5
01-02-2012   29-02-2012          162.5

是否有任何方法可以在不使用PLSQL的情况下实现这一点?您可以使用滞后函数来确定一行和上一个排序行之间的每日平均值

一旦你有了一个每日平均数,你就可以乘以这个时期的天数


都在同一个sql语句中。

您可以使用滞后函数来确定一行和上一个排序行之间的每日平均值

一旦你有了一个每日平均数,你就可以乘以这个时期的天数


都在同一个sql语句中。

我不确定您希望如何计算这些值,但首先,请尝试每月打破记录:

with dts as (select last_day(add_months(
  to_date('20111201','yyyymmdd'),level)) ld,
             add_months(
  to_date('20111201','yyyymmdd'),level) sd
             from dual connect by level < 12)
select case when start_date >= sd then start_date  else sd end st_dt,
case when end_date <= ld then end_date  else ld end en_dt, amount,
ld-sd days_in_month,
case when end_date <= ld then end_date  else ld end-
case when start_date >= sd then start_date  else sd end part
from t, dts
where (start_date >= sd and to_char(start_date, 'yyyymm') = 
       to_char(sd, 'yyyymm'))
       or (end_date <= ld and to_char(end_date, 'yyyymm') = 
       to_char(ld, 'yyyymm'))

我不确定您希望如何计算这些值,但首先,请尝试每月打破记录:

with dts as (select last_day(add_months(
  to_date('20111201','yyyymmdd'),level)) ld,
             add_months(
  to_date('20111201','yyyymmdd'),level) sd
             from dual connect by level < 12)
select case when start_date >= sd then start_date  else sd end st_dt,
case when end_date <= ld then end_date  else ld end en_dt, amount,
ld-sd days_in_month,
case when end_date <= ld then end_date  else ld end-
case when start_date >= sd then start_date  else sd end part
from t, dts
where (start_date >= sd and to_char(start_date, 'yyyymm') = 
       to_char(sd, 'yyyymm'))
       or (end_date <= ld and to_char(end_date, 'yyyymm') = 
       to_char(ld, 'yyyymm'))

我看到A.B.凯德抢先一步,但我的解决方案是:

SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MM-YYYY'
  2  /

Session altered.

SQL> CREATE TABLE t (start_date DATE, end_date DATE, amount NUMBER);

Table created.

SQL> INSERT INTO t VALUES (TO_DATE('20120115','YYYYMMDD'),TO_DATE('20120125','YYYYMMDD'),100);

1 row created.

SQL> INSERT INTO t VALUES (TO_DATE('20120126','YYYYMMDD'),TO_DATE('20120210','YYYYMMDD'),100);

1 row created.

SQL> INSERT INTO t VALUES (TO_DATE('20120211','YYYYMMDD'),TO_DATE('20120229','YYYYMMDD'),100);

1 row created.

SQL>
设置了一些测试数据

SQL> COL for_month       FOR A9
SQL> COL pro_rated_start FOR A15
SQL> COL pro_rated_end   FOR A13
SQL>
。。。并格式化了一些列

SQL> WITH months AS (
  2      SELECT TRUNC(MIN(start_date),'MM') min_start_month
  3      ,      MONTHS_BETWEEN(TRUNC(MAX(end_date),'MM'),TRUNC(MIN(start_date),'MM')) + 1 mos
  4      FROM   t
  5  )
  6  , offset AS (
  7      SELECT ROWNUM - 1 r
  8      FROM  (SELECT NULL
  9             FROM   DUAL
 10             CONNECT BY LEVEL <= (SELECT mos FROM months))
 11  )
 12  , ranges AS (
 13      SELECT  ADD_MONTHS(months.min_start_month, offset.r)                mo_start
 14      ,       LAST_DAY(ADD_MONTHS(months.min_start_month, offset.r))      mo_end
 15      FROM    offset
 16      ,       months
 17  )
 18  SELECT      TO_CHAR(GREATEST(t.start_date,ranges.mo_start),'Mon YYYY') for_month
 19  ,           MIN(GREATEST(t.start_date,ranges.mo_start))                pro_rated_start
 20  ,           MAX(LEAST(t.end_date,ranges.mo_end))                       pro_rated_end
 21  ,           SUM(t.amount
 22                  * CASE
 23                    WHEN t.end_date < ranges.mo_end
 24                     AND t.start_date > ranges.mo_start
 25                    THEN 1
 26                    ELSE ((LEAST(t.end_date,ranges.mo_end)
 27                        - GREATEST(t.start_date,ranges.mo_start) + 1)
 28                       / (t.end_date - t.start_date + 1))
 29                    END)  pro_rated_amount
 30  FROM        t
 31  ,           ranges
 32  WHERE       t.start_date <= ranges.mo_end
 33  AND         t.end_date >= ranges.mo_start
 34  GROUP BY    TO_CHAR(GREATEST(t.start_date,ranges.mo_start),'Mon YYYY');

FOR_MONTH PRO_RATED_START PRO_RATED_END PRO_RATED_AMOUNT
--------- --------------- ------------- ----------------
Jan 2012  15-01-2012      31-01-2012               137.5
Feb 2012  01-02-2012      29-02-2012               162.5

SQL>

我看到A.B.凯德抢先一步,但我的解决方案是:

SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MM-YYYY'
  2  /

Session altered.

SQL> CREATE TABLE t (start_date DATE, end_date DATE, amount NUMBER);

Table created.

SQL> INSERT INTO t VALUES (TO_DATE('20120115','YYYYMMDD'),TO_DATE('20120125','YYYYMMDD'),100);

1 row created.

SQL> INSERT INTO t VALUES (TO_DATE('20120126','YYYYMMDD'),TO_DATE('20120210','YYYYMMDD'),100);

1 row created.

SQL> INSERT INTO t VALUES (TO_DATE('20120211','YYYYMMDD'),TO_DATE('20120229','YYYYMMDD'),100);

1 row created.

SQL>
设置了一些测试数据

SQL> COL for_month       FOR A9
SQL> COL pro_rated_start FOR A15
SQL> COL pro_rated_end   FOR A13
SQL>
。。。并格式化了一些列

SQL> WITH months AS (
  2      SELECT TRUNC(MIN(start_date),'MM') min_start_month
  3      ,      MONTHS_BETWEEN(TRUNC(MAX(end_date),'MM'),TRUNC(MIN(start_date),'MM')) + 1 mos
  4      FROM   t
  5  )
  6  , offset AS (
  7      SELECT ROWNUM - 1 r
  8      FROM  (SELECT NULL
  9             FROM   DUAL
 10             CONNECT BY LEVEL <= (SELECT mos FROM months))
 11  )
 12  , ranges AS (
 13      SELECT  ADD_MONTHS(months.min_start_month, offset.r)                mo_start
 14      ,       LAST_DAY(ADD_MONTHS(months.min_start_month, offset.r))      mo_end
 15      FROM    offset
 16      ,       months
 17  )
 18  SELECT      TO_CHAR(GREATEST(t.start_date,ranges.mo_start),'Mon YYYY') for_month
 19  ,           MIN(GREATEST(t.start_date,ranges.mo_start))                pro_rated_start
 20  ,           MAX(LEAST(t.end_date,ranges.mo_end))                       pro_rated_end
 21  ,           SUM(t.amount
 22                  * CASE
 23                    WHEN t.end_date < ranges.mo_end
 24                     AND t.start_date > ranges.mo_start
 25                    THEN 1
 26                    ELSE ((LEAST(t.end_date,ranges.mo_end)
 27                        - GREATEST(t.start_date,ranges.mo_start) + 1)
 28                       / (t.end_date - t.start_date + 1))
 29                    END)  pro_rated_amount
 30  FROM        t
 31  ,           ranges
 32  WHERE       t.start_date <= ranges.mo_end
 33  AND         t.end_date >= ranges.mo_start
 34  GROUP BY    TO_CHAR(GREATEST(t.start_date,ranges.mo_start),'Mon YYYY');

FOR_MONTH PRO_RATED_START PRO_RATED_END PRO_RATED_AMOUNT
--------- --------------- ------------- ----------------
Jan 2012  15-01-2012      31-01-2012               137.5
Feb 2012  01-02-2012      29-02-2012               162.5

SQL>
这是一次尝试:

我和几个月前有过一次聚会。如果一行在两个月内将被复制,更准确地说,将在两个月内分发。 对于每行*月,我应用公式“当前行的金额/天数*当前月的天数”

这段代码经过测试,给出了您想要的结果。

这是一次尝试:

我和几个月前有过一次聚会。如果一行在两个月内将被复制,更准确地说,将在两个月内分发。 对于每行*月,我应用公式“当前行的金额/天数*当前月的天数”


此代码经过测试,并给出了您想要的结果。

抱歉-我看不出您从哪里获得62.5这样的值?请使用伪代码,而不是直接获得值,进行更好的解释。加上二月份的微积分。对不起,我看不出你从哪里得到像62.5这样的值?请更好地解释,使用伪代码,而不是直接得到值。加上2月份的演算。