简体   繁体   English

SQL查询以查找最大获利时间表

[英]SQL Query to find the Maximum profit schedule

Input data 输入数据

create table schedule_profile 
(
    start_date date,
    end_date date,
    profit number(38,0)
);

insert into schedule_profile values ('5-Jan-2018','1-May-2018',100);
insert into schedule_profile values ('6-Jan-2018','10-Feb-2018',50);
insert into schedule_profile values ('11-Feb-2018','28-Feb-2018',150);
insert into schedule_profile values ('2-May-2018','30-May-2018',200);
insert into schedule_profile values ('6-Jan-2018','30-Jan-2018',25);

Output 产量

    '6-Jan-2018','10-Feb-2018',50
    '11-Feb-2018','28-Feb-2018',150
    '2-May-2018','30-May-2018',200

Question: 题:

What is/are the schedule(s) that a user can pick up so that he can get maximum profit? 用户可以选择什么时间表以获取最大的利润?

Note : If a user picks a schedule, they can not pickup an overlapping schedule. 注意 :如果用户选择一个计划,则不能选择重叠的计划。

Consider each row value as a schedule. 将每个行的值视为时间表。 Example, if a User picks up the first schedule, he will work from 5-Jan-2018' to '1-May-2018' and he will earn a profit of 100. But instead, if the picks the schedules as per the output, he will earn a profit 例如,如果用户选择了第一个时间表,则他将从2018年1月5日到2018年5月1日工作,他将获得100的利润。但是,如果用户根据输出选择时间表,他会赚钱

While I tried with Analytical Window Functions and self joins, I could not resolve the problem. 当我尝试使用“分析窗口功能”和自连接时,无法解决问题。 Is there any way to resolve this in SQL? 有什么办法可以在SQL中解决此问题?

Here's one way to approach this: 这是解决此问题的一种方法:

Using match_recognize , you can find the non-overlapping dates. 使用match_recognize ,您可以找到不重叠的日期。 To do this: 去做这个:

  • Sort the input by start_date, end_date 按开始日期,结束日期对输入进行排序
  • A row overlaps with the previous if it's start_date is before the end_date of the previous (non-overlapping) row. 如果start_date在上一行(不重叠)的end_date之前,则该行与上一行重叠。 So you can get the non-overlapping dates by defining a pattern variable non_overlap as ( start_date >= non_overlap.end_date or start_date >= first ( end_date ) ) 因此,您可以通过将模式变量non_overlap as ( start_date >= non_overlap.end_date or start_date >= first ( end_date ) )定义non_overlap as ( start_date >= non_overlap.end_date or start_date >= first ( end_date ) )来获得非重叠日期。
  • Find all the non-overlapping rows, skipping over the overlapped ones with the pattern init ( non_overlap | {-overlap-} )* (curly braces surrounding minus is the exclusion operator, stopping you including overlapped dates in the output) 查找所有非重叠的行,并使用模式init ( non_overlap | {-overlap-} )*跳过重叠的行(负号的花括号是排除运算符,使您init ( non_overlap | {-overlap-} )*在输出中包括重叠的日期)
  • You need to repeat this for every row in the input data set. 您需要对输入数据集中的每一行重复此操作。 So you need after match skip to next row 所以after match skip to next row您需要after match skip to next row
  • Get the total profit for each group by returning the nvl ( final sum ( non_overlap.profit ), 0 ) + init.profit in the measures clause 通过返回measures子句中的nvl ( final sum ( non_overlap.profit ), 0 ) + init.profit来获得每个组的总利润。

Which gives something like: 这给出了类似的内容:

select * 
from   schedule_profile
  match_recognize (
    order by start_date, end_date
    measures 
      classifier() cls,
      match_number() grp,
      nvl ( final sum ( non_overlap.profit ), 0 ) 
        + init.profit total_profit
    all rows per match 
    after match skip to next row
    pattern ( init ( non_overlap | {-overlap-} )* )
    define 
      non_overlap as ( 
        start_date >= non_overlap.end_date or
        start_date >= first ( end_date ) 
      )
);

START_DATE     END_DATE       CLS            GRP    TOTAL_PROFIT    PROFIT   
05-JAN-2018    01-MAY-2018    INIT                1             300       100 
02-MAY-2018    30-MAY-2018    NON_OVERLAP         1             300       200 

06-JAN-2018    30-JAN-2018    INIT                2             375        25 
11-FEB-2018    28-FEB-2018    NON_OVERLAP         2             375       150 
02-MAY-2018    30-MAY-2018    NON_OVERLAP         2             375       200 

06-JAN-2018    10-FEB-2018    INIT                3             400        50 
11-FEB-2018    28-FEB-2018    NON_OVERLAP         3             400       150 
02-MAY-2018    30-MAY-2018    NON_OVERLAP         3             400       200 

11-FEB-2018    28-FEB-2018    INIT                4             350       150 
02-MAY-2018    30-MAY-2018    NON_OVERLAP         4             350       200 

02-MAY-2018    30-MAY-2018    INIT                5             200       200

Armed with this, all you need to do is find the max total_profit. 有了这个,您要做的就是找到max total_profit。 Which you can do by making it an analytic by adding over () . 您可以通过添加over ()使其成为分析对象来实现。 Stick it in a subquery and return the rows where the total profit equals this max: 将其粘贴在子查询中,并返回总利润等于该最大值的行:

with profits as (
  select p.*, 
         max ( total_profit ) 
           over () max_total_profit
  from   schedule_profile
    match_recognize (
      order by start_date, end_date
      measures 
        classifier() cls,
        match_number() grp,
        nvl ( final sum ( non_overlap.profit ), 0 ) 
          + init.profit total_profit
      all rows per match 
      after match skip to next row
      pattern ( init ( non_overlap | {-overlap-} )* )
      define 
        non_overlap as ( 
          start_date >= non_overlap.end_date or
          start_date >= first ( end_date ) 
        )
  ) p
)
  select * from profits
  where  total_profit = max_total_profit;

START_DATE     END_DATE       CLS            GRP    TOTAL_PROFIT    PROFIT    MAX_TOTAL_PROFIT   
06-JAN-2018    10-FEB-2018    INIT                3             400        50                 400 
11-FEB-2018    28-FEB-2018    NON_OVERLAP         3             400       150                 400 
02-MAY-2018    30-MAY-2018    NON_OVERLAP         3             400       200                 400

Note : this can process the same row many times. 注意 :这可以处理同一行很多次。 On large data sets with many overlaps, this could be very slow! 在具有许多重叠的大型数据集上,这可能会非常慢!

Using recursive cte 使用递归CTE

with t (start_date, end_date, total, path) as(
    select start_date, end_date, profit as total, start_date || '..' || end_date || ';' as path
    from schedule_profile
    union all
    select sp.start_date, sp.end_date, profit + total, path || sp.start_date || '..' || sp.end_date || ';'
    from t
    join schedule_profile sp on t.end_date < sp.start_date
)
select path, total  
from (
    select path, total, max(total) over() maxtotal
    from t
) tm
where total = maxtotal ;

returns 回报

PATH    TOTAL
06-JAN-18..10-FEB-18;11-FEB-18..28-FEB-18;02-MAY-18..30-MAY-18; 400

Fiddle 小提琴

You can use the following query to get the desired result but in my case, it is in single rows. 您可以使用以下查询来获得所需的结果,但就我而言,它是单行的。

-- DATA PREPARATION
SQL> create table schedule_profile
  2  (
  3      start_date date,
  4      end_date date,
  5      profit number(38,0)
  6  );

Table created.

SQL>
SQL> insert into schedule_profile values ('5-Jan-2018','1-May-2018',100);

1 row created.

SQL> insert into schedule_profile values ('6-Jan-2018','10-Feb-2018',50);

1 row created.

SQL> insert into schedule_profile values ('11-Feb-2018','28-Feb-2018',150);

1 row created.

SQL> insert into schedule_profile values ('2-May-2018','30-May-2018',200);

1 row created.

SQL> insert into schedule_profile values ('6-Jan-2018','30-Jan-2018',25);

1 row created.

-- -

-- ACTUAL QUERY
SQL>
SQL> SELECT
  2      LTRIM(PTH, ' | ') AS DATES,
  3      XMLQUERY
  4  ( VAL RETURNING CONTENT ). GETNUMBERVAL() AS VAL
  5  FROM
  6      (
  7          SELECT
  8              SYS_CONNECT_BY_PATH(START_DATE || '-'
  9                                                || END_DATE || '(' || PROFIT || ')', ' | ') AS PTH,
 10              LTRIM(SYS_CONNECT_BY_PATH(PROFIT, '+'), '+') AS VAL
 11          FROM
 12              SCHEDULE_PROFILE
 13          CONNECT BY
 14              START_DATE > PRIOR END_DATE
 15      )
 16  ORDER BY VAL DESC FETCH FIRST ROW ONLY;

DATES
--------------------------------------------------------------------------------
       VAL
----------
06-JAN-18-10-FEB-18(50) | 11-FEB-18-28-FEB-18(150) | 02-MAY-18-30-MAY-18(200)
       400


SQL>

db<>fiddle demo db <> fiddle演示

Cheers!! 干杯!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM