如何从每月目标中提取随机日期 targets/sales 数据

How to extract random dates targets/sales data from monthly targets

我为全年的不同项目类别定义了每月目标。

示例:

January Target for A Category - 15,000
January Target for R Category - 10,000
January Target for O Category - 5,000

Actual Sales for A Category January - 18,400
Actual Sales for R Category January - 8,500
Actual Sales for O Category January - 3,821

将实际销售额与目标进行比较的 SQL 查询将很简单,如下所示:

  SELECT   TO_CHAR (Sales_Date, 'MM') Sales_Month,
           Sales_Category,
           SUM (Sales_Value) Sales_Val_Monthly,
           Target_Month,
           Target_Category,
           Target_Value
    FROM   Sales_Data, Target_Data
   WHERE   TO_CHAR (Sales_Date, 'MM') = Target_Month
           AND Sales_Category = Target_Category
GROUP BY   TO_CHAR (Sales_Date, 'MM'),
           Target_Month,
           Target_Category,
           Sales_Category,
           Target_Value;

现在我要求用户在报告参数中输入FROM_DATETILL_DATE并且starting/ending日期可以是随机的,它不会代表完整的一个月或一周,开始日期可以是12/01/2018,结束日期可以是15/01/2018,即4天的数据。结果应该计算4天的实际数据,计算4天的目标,考虑到会有6个工作日(星期日是假期),如果日期范围包括星期日,则不予考虑。

此外,应考虑一个月中的天数,日期参数可能包含一个月中的一些天数和另一个月中的一些天数,或者可能超过一个月。

Target_Table (Target_Data)          
Target_Year      Target_Month      Target_Category Target_Value
2018            01                 A                15000
2018            02                 A                8500 
2018            03                 A                9500 
2018            01                 R                15000
2018            02                 R                8500 
2018            03                 R                9500 
2018            01                 O                15000
2018            02                 O                8500 
2018            03                 O                9500 


Sales Table (Sales_Data)                                    
Inv_Txn         Inv_No    Sales_Date      Item_Code Sales_Category    Qty  Rate    Sales_Value  Inv_Locn Inv_SM_ID
A21            2018000001 02/01/2018      XXXX         A               2   5.5      11             O001     XXXX
R32            2018000001 27/02/2018      XXXX         R               3   9.5      28.5           O305     XXXX
O98            2018000001 12/03/2018      XXXX         O               12  12.5    150             O901     XXXX
U76            2018000001 18/01/2018      XXXX         A               98  5.5     539             O801     XXXX
B87            2018000001 19/02/2018      XXXX         R               2   9.5      19             O005     XXXX
A21            2018000002 13/03/2018      XXXX         R               45  9.5     427.5           O001     XXXX
B87            2018000002 14/03/2018      XXXX         O               12  12.5    150             O005     XXXX

Desired Output (From Date: 27/02/2018 Till Date: 06/03/2018)        
 Target_Category Target_Value      Sales_Value
    A             87.52            21.88
    A             96.25            24.06
    A             74.25            18.56
    R             100.25           25.06
    R             800.2            200.05
    R             25.1             6.28
    O             75.5             18.88
    O             98.1             24.53
    O             25.5             6.38

您可以使用以下查询计算两个日期之间的工作日数。我通过名为 table 添加了一个非工作日期:holiday_dates 并创建了从 12/01/2018 到 15/01 的一系列日期。我删除那些星期日或假期的日期。请让我知道它是否适合您。谢谢

create table holiday_dates(holiday_dte date, holiday_desc varchar(100));
insert into holiday_dates values(TO_DATE('13/01/2018','DD-MM-YYYY'), 'Not a Working Day');

With tmp as (
select count(*) as num_of_working_days 
from ( select rownum  as rn
from all_objects 
where rownum <= to_date('15/01/2018','DD-MM-YYYY') - to_date('12/01/2018','DD-MM-YYYY')+1 ) 
where to_char( to_date('12/01/2018','DD-MM-YYYY')+rn-1, 'DY' ) not in ( 'SUN' ) 
and not exists ( select null from holiday_dates where holiday_dte = trunc(to_date('12/01/2018','DD-MM-YYYY') + rn - 1)))
SELECT   TO_CHAR (Sales_Date, 'MM') Sales_Month,
           Sales_Category,
           SUM (Sales_Value) Sales_Val_Monthly,
           Target_Month,
           Target_Category,
           Target_Value,
           tmp.num_of_working_days 
    FROM   Sales_Data, Target_Data, tmp
   WHERE   Sales_Date between to_date('12/01/2018','DD-MM-YYYY') and to_date('15/01/2018','DD-MM-YYYY')
           AND Sales_Category = Target_Category
GROUP BY   TO_CHAR (Sales_Date, 'MM'),
           Target_Month,
           Target_Category,
           Sales_Category,
           Target_Value;

第一步可能是看看我们是否可以获得给定月份的星期日数。事实证明,我们可以 - 而且我们不必使用任何 SQL 技巧或 PL/SQL:

SELECT EXTRACT( DAY FROM LAST_DAY(SYSDATE) ) AS month_day_cnt
     , CEIL( ( LAST_DAY(TRUNC(SYSDATE, 'MONTH')) - NEXT_DAY(TRUNC(SYSDATE, 'MONTH')-1, 'SUN') + 1 ) / 7 ) AS sunday_cnt
  FROM dual;

这将为我们提供给定月份的天数以及星期日的数量。我们需要做的就是从前者中减去后者以获得工作日数。我们可以将其用于您的初始查询(顺便说一下,我建议使用 TRUNC() 而不是 TO_CHAR(),因为您的用户可能需要跨越一个日历年以上的日期范围):

SELECT TRUNC(s.Sales_Date, 'MONTH') AS Sales_Month
     , EXTRACT( DAY FROM LAST_DAY( TRUNC(s.Sales_Date, 'MONTH') ) ) - CEIL( ( LAST_DAY(TRUNC(s.Sales_Date, 'MONTH')) - NEXT_DAY(TRUNC(s.Sales_Date, 'MONTH')-1, 'SUN') + 1 ) / 7 ) AS working_day_cnt
     , s.Sales_Category, SUM(s.Sales_Value) AS Sales_Val_Monthly
     , t.Target_Value -- Target_Month and Target_Category are superfluous
  FROM Sales_Data s INNER JOIN Target_Data t
    ON TO_CHAR(s.Sales_Date, 'MM') = t.Target_Month
   AND TO_CHAR(s.Sales_Date, 'YYYY') = t.Target_Year
   AND s.Sales_Category = t.Target_Category
 GROUP BY TRUNC(s.Sales_Date, 'MONTH'), Sales_Category, Target_Value;

现在给定开始日期和结束日期,我们可以生成这些日期之间所有月份的工作日数,如下所示:

SELECT TRUNC(range_dt, 'MONTH'), COUNT(*) FROM (
    SELECT start_dt + LEVEL - 1 AS range_dt
      FROM dual
   CONNECT BY start_dt + LEVEL - 1 < end_dt
) WHERE TO_CHAR(range_dt, 'DY') != 'SUN'
 GROUP BY TRUNC(range_dt, 'MONTH');

其中 start_dtend_dt 是用户提供的参数。将所有这些放在一起,我们将得到如下内容:

WITH rd ( range_month, range_day_cnt ) AS (
    SELECT TRUNC(range_dt, 'MONTH'), COUNT(*) FROM (
        SELECT start_dt + LEVEL - 1 AS range_dt
          FROM dual
       CONNECT BY start_dt + LEVEL - 1 < end_dt
    ) WHERE TO_CHAR(range_dt, 'DY') != 'SUN'
     GROUP BY TRUNC(range_dt, 'MONTH')
)
SELECT range_month, Sales_Category, Sales_Val_Monthly
     , range_day_cnt, working_day_cnt, Target_Value
     , Target_Value*range_day_cnt/working_day_cnt AS prorated_target_value
  FROM (
    SELECT r.range_month, r.range_day_cnt
         , EXTRACT( DAY FROM LAST_DAY( TRUNC(s.Sales_Date, 'MONTH') ) ) - CEIL( ( LAST_DAY(TRUNC(s.Sales_Date, 'MONTH')) - NEXT_DAY(TRUNC(s.Sales_Date, 'MONTH')-1, 'SUN') + 1 ) / 7 ) AS working_day_cnt
         , s.Sales_Category, SUM(s.Sales_Value) AS Sales_Val_Monthly
         , t.Target_Value -- Target_Month and Target_Category are superfluous
      FROM rd INNER JOIN Sales_Data s
        ON rd.range_month = TRUNC(s.Sales_Date, 'MONTH')
     INNER JOIN Target_Data t
        ON TO_CHAR(s.Sales_Date, 'MM') = t.Target_Month
       AND TO_CHAR(s.Sales_Date, 'YYYY') = t.Target_Year
       AND s.Sales_Category = t.Target_Category
     WHERE s.Sales_Date >= TRUNC(start_dt)
       AND s.Sales_Date < TRUNC(end_dt+1)
     GROUP BY r.range_month, r.range_day_cnt, s.Sales_Category, t.Target_Value
) ORDER BY range_month;

如果您有一个 table 的 public 假期,那么这些也必须在某个地方被考虑在内——在 rd 常见的 table 表达式和从工作日计算。如果以上没有给你一个开始,那么我可以再看一下,看看其他假期是如何工作的。