简体   繁体   English

发生重叠的日期范围时,PHP求和金额

[英]PHP Summing amounts when overlapping date ranges are occuring

I have a MySQL table that looks like this: 我有一个如下所示的MySQL表:

record_id    amount    start_date    end_date
1            20000     2018-01-01   2018-12-01
1            -15000    2018-02-01   2018-04-01
1            50000     2018-04-02   2018-06-30

This gets translated in PHP to an array that looks like this: 这在PHP中转换为如下所示的数组:

[1] => [
    [
       "start_date" => "2018-01-01",
       "end_date" => "2018-12-01",
       "amount"   => 20000
    ],
    [
       "start_date" => "2018-02-01",
       "end_date" => "2018-04-01",
       "amount"   => -15000
    ],
    [
       "start_date" => "2018-04-02",
       "end_date" => "2018-06-30",
       "amount"   => 50000
    ],
]

The problem is, I need to sum overlapping amounts in the date ranges, and keep an original amount for the longest date range. 问题是,我需要对日期范围内的重叠金额进行总计,并为最长的日期范围保留原始金额。 So the resulting MySQL table would look like this: 因此,生成的MySQL表将如下所示:

record_id    amount   start_date    end_date
1            20000    2018-01-01    2018-01-30
1            5000     2018-02-01    2018-04-01 
1            70000    2018-04-02    2018-06-30
1            20000    2018-07-01    2018-12-01

And the resulting PHP array would look like this: 生成的PHP数组如下所示:

[1] => [
    [
       "start_date" => "2018-01-01",
       "end_date" => "2018-01-30",
       "amount"   => 20000
    ],
    [
       "start_date" => "2018-02-01",
       "end_date" => "2018-04-01",
       "amount"   => 5000
    ],
    [
       "start_date" => "2018-04-02",
       "end_date" => "2018-06-30",
       "amount"   => 70000
    ],
    [
       "start_date" => "2018-07-01",
       "end_date" => "2018-12-01",
       "amount"   => 20000 
    ],
]

Basically, the amount from the first start date to the day before the next start date is the current amount. 基本上,从第一个开始日期到下一个开始日期的前一天的金额就是当前金额。 Then for any overlapping date range, the amount gets summed. 然后,对于任何重叠的日期范围,该金额将相加。 So 20,000 + -15,000 = 5,000. 因此20,000 + -15,000 = 5,000。 Then, for the date range 2018-04-02 to 2018-06-30, the initial amount (still valid since it runs through december) will be added to the 50,000, making it 70,000. 然后,对于日期范围为2018-04-02至2018-06-30的初始金额(自12月份起仍然有效)将被添加到50,000中,使其达到70,000。 Finally, the end date is still running so we make a new entry for 20,000. 最后,结束日期仍在运行,因此我们输入了20,000个新条目。

I could make pseudo-code for a single case like this, but these cases are all dynamic, and the overlapping dates and ranges could change. 我可以为单个案例制作伪代码,但是这些案例都是动态的,重叠的日期和范围可能会发生变化。

Apparently, it's impossible / very hard to do this in MySQL, so I was wondering if anyone had experience doing these overlapping date ranges in PHP. 显然,在MySQL中不可能/很难做到这一点,所以我想知道是否有人在PHP中进行过这些重叠的日期范围的体验。

Edit: I've marked strawberry's answer as correct as it works. 编辑:我已经将草莓的答案标记为正确,因为它起作用了。 If possible, he can paste this with his answer and I can remove this edit, but there is some work to be done before his answer works (pre mysql 8.0). 如果可能,他可以在答案中粘贴该内容,并且可以删除此编辑,但是在答案有效之前(mysql 8.0之前),还需要做一些工作。 You can run this script to get his answer to work, before doing anything else (source is slightly modified from this version ): 您可以先执行以下脚本来获得他的答案,然后再执行其他任何操作( 此版本中的源略有修改):

DROP TABLE IF EXISTS calendar;
CREATE TABLE calendar(
        id                      INTEGER PRIMARY KEY,  -- year*10000+month*100+day
        dt                      DATE NOT NULL,
        year                    INTEGER NOT NULL,
        month                   INTEGER NOT NULL, -- 1 to 12
        day                     INTEGER NOT NULL, -- 1 to 31
        quarter                 INTEGER NOT NULL, -- 1 to 4
        week                    INTEGER NOT NULL, -- 1 to 52/53
        day_name                VARCHAR(9) NOT NULL, -- 'Monday', 'Tuesday'...
        month_name              VARCHAR(9) NOT NULL, -- 'January', 'February'...
        holiday_flag            CHAR(1) DEFAULT 'f' CHECK (holiday_flag in ('t', 'f')),
        weekend_flag            CHAR(1) DEFAULT 'f' CHECK (weekend_flag in ('t', 'f')),
        event                   VARCHAR(50),
        UNIQUE td_ymd_idx (year,month,day),
        UNIQUE td_dt_idx (dt)

) Engine=MyISAM;

DROP PROCEDURE IF EXISTS fill_date_dimension;
DELIMITER //
CREATE PROCEDURE fill_date_dimension(IN startdate DATE,IN stopdate DATE)
BEGIN
    DECLARE currentdate DATE;
    SET currentdate = startdate;
    WHILE currentdate < stopdate DO
        INSERT INTO calendar VALUES (
                        YEAR(currentdate)*10000+MONTH(currentdate)*100 + DAY(currentdate),
                        currentdate,
                        YEAR(currentdate),
                        MONTH(currentdate),
                        DAY(currentdate),
                        QUARTER(currentdate),
                        WEEKOFYEAR(currentdate),
                        DATE_FORMAT(currentdate,'%W'),
                        DATE_FORMAT(currentdate,'%M'),
                        'f',
                        CASE DAYOFWEEK(currentdate) WHEN 1 THEN 't' WHEN 7 then 't' ELSE 'f' END,
                        NULL);
        SET currentdate = ADDDATE(currentdate,INTERVAL 1 DAY);
    END WHILE;
END
//
DELIMITER ;

TRUNCATE TABLE calendar;

CALL fill_date_dimension('1-01-01','2040-01-01');
OPTIMIZE TABLE calendar;

Here's a solution that uses a simple calendar(dt) utility table... 这是使用简单的calendar(dt)实用程序表的解决方案...

DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table
(start_date DATE NOT NULL PRIMARY KEY
,end_date DATE NOT NULL
,amount INT NOT NULL
);

INSERT INTO my_table VALUES
('2018-01-01','2018-12-01', 20000),
('2018-02-01','2018-04-01',-15000),
('2018-04-02','2018-06-30', 50000);

SELECT MIN(dt) range_start
     , MAX(dt) range_end
     , MIN(amount) amount
  FROM
     ( 
     SELECT dt
     , amount
     , CASE WHEN @prev=amount THEN @i:=@i ELSE @i:=@i+1 END i
     , @prev:=amount
  FROM 
     ( SELECT x.*
            , SUM(y.amount) amount 
         FROM calendar x 
         JOIN my_table y 
           ON x.dt BETWEEN y.start_date AND y.end_date 
        GROUP 
           BY x.dt 
      ) a
   JOIN (SELECT @prev:=null,@i:=0) vars
 ORDER 
    BY dt
   ) n
   GROUP 
     BY i;

+-------------+------------+--------+
| range_start | range_end  | amount |
+-------------+------------+--------+
| 2018-01-01  | 2018-01-31 |  20000 |
| 2018-02-01  | 2018-04-01 |   5000 |
| 2018-04-02  | 2018-06-30 |  70000 |
| 2018-07-01  | 2018-12-01 |  20000 |
+-------------+------------+--------+

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

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