在MYSQL中将日期转换为日期范围---如何处理日期中的间隙

Posted

技术标签:

【中文标题】在MYSQL中将日期转换为日期范围---如何处理日期中的间隙【英文标题】:Transform Dates into Date Range in MYSQL---how to handle gaps in the dates 【发布时间】:2017-07-10 23:46:58 【问题描述】:

我正在寻求将以下数据转换为所需输出的帮助。我们有 Item,LOC DAY 级别的数据,需要转换为 Item,Loc Date Range 以减少表中的记录数和其他要求。

Item        LOC  RP_DATE    RP_IND   
1003785256  543 2016-11-05  Y
1003785256  543 2016-11-06  Y
1003785256  543 2016-11-07  Y
1003785256  543 2016-11-09  Y
1003785256  543 2016-11-10  Y
1003790365  150 2016-11-05  Y
1003797790  224 2016-11-05  Y
1003797790  224 2016-11-06  Y
1003797790  224 2016-11-07  Y
1003797790  224 2016-11-08  Y

所需输出:

Item        LOC  RP_ST_DATE    RP_END_DATE   
1003785256  543 2016-11-05   2016-11-07
1003785256  543 2016-11-09   2016-11-10
1003790365  150 2016-11-05   2016-11-05
1003797790  224 2016-11-05   2016-11-08

【问题讨论】:

通过 epm_sku_idnt,loc_idnt 从 RP_SKU_LOC 组中选择 epm_sku_idnt,loc_idnt,min(rp_date),max(rp_date);我试过这个,但它不适用于有差距的场景 看看:***.com/questions/44979192/… 和 ***.com/questions/37401369/…。请注意,这些答案适用于 SQL Server。 您还需要在问题中清楚地描述您要达到的目标。 我正在尝试将 DAY 级别的数据转换为 DATE RANGE 级别,如预期输出所示。 @Harsha 您似乎忽略了问题中的一个关键要求:如何处理日期中的空白。您的一个 cmets 给出了提示,但您的问题仍未具体说明。 【参考方案1】:

这种方法适用于 mysql。它使用有序子查询中的组合变量来为每个“范围”建立一个共同的开始日期。 CROSS JOIN 仅用于初始化变量,它不会改变行数。一旦确定了共同的开始日期,它就会成为外部查询中的简单分组查询。

SELECT Item, LOC, RP_IND, dr_begin, MAX(RP_DATE) dr_end
FROM (
  SELECT
         mytable.*
       , @fin := CONVERT(IF(@item<=>item AND @loc<=>loc AND DATEDIFF(rp_date, @d)=1, @fin, rp_date), DATE) AS dr_begin
       , @item := item
       , @loc := loc
       , @d := rp_date
  FROM     mytable CROSS JOIN (SELECT @item:=NULL, @loc:=NULL, @d:=NULL, @fin := NULL) AS init
  ORDER BY item, loc, rp_date
  ) d
GROUP BY  Item, LOC, RP_IND, dr_begin
;

+----+------------+-----+--------+------------+---------------------+
|    |    Item    | LOC | RP_IND |  dr_begin  |       dr_end        |
+----+------------+-----+--------+------------+---------------------+
|  1 | 1003785256 | 543 | Y      | 2016-11-05 | 07.11.2016 00:00:00 |
|  2 | 1003785256 | 543 | Y      | 2016-11-09 | 10.11.2016 00:00:00 |
|  3 | 1003790365 | 150 | Y      | 2016-11-05 | 05.11.2016 00:00:00 |
|  4 | 1003797790 | 224 | Y      | 2016-11-05 | 08.11.2016 00:00:00 |
+----+------------+-----+--------+------------+---------------------+

注意 returns 1 if both operands are NULL

查看查询:http://rextester.com/SEYG96251

#drop table mytable;

CREATE TABLE mytable(
   Item    INTEGER  NOT NULL
  ,LOC     INTEGER  NOT NULL
  ,RP_DATE DATE  NOT NULL
  ,RP_IND  VARCHAR(1) NOT NULL
);
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003785256,543,'2016-11-05','Y');
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003785256,543,'2016-11-06','Y');
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003785256,543,'2016-11-07','Y');
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003785256,543,'2016-11-09','Y');
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003785256,543,'2016-11-10','Y');
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003790365,150,'2016-11-05','Y');
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003797790,224,'2016-11-05','Y');
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003797790,224,'2016-11-06','Y');
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003797790,224,'2016-11-07','Y');
INSERT INTO mytable(Item,LOC,RP_DATE,RP_IND) VALUES (1003797790,224,'2016-11-08','Y');

【讨论】:

当表数据大约为 10M 时,此逻辑工作正常...但我有一个包含 500M 到 1B 记录的表,此查询挂起并给我一个错误,要求增加堆大小...我将堆大小增加到 32GB,但仍然出现相同的错误。 @rsh v.reretable 当然,但 MySQL 8 之前的版本。?当 'row_number() over ()` 可用时缺少 window functions。如果您的数据库支持 row_number 使用它。遗憾的是,任何替代方案对于您的 v.large 表来说都是丑陋的。请注意,我还没有看到或使用过 MySQL v8。谁知道以这种规模实现窗口功能的效果如何。许多其他数据库已经具备此功能。

以上是关于在MYSQL中将日期转换为日期范围---如何处理日期中的间隙的主要内容,如果未能解决你的问题,请参考以下文章

无法在 MySQL 查询中将字符串转换为日期时间

如何在 MySQL 中将字符串转换为日期?

PHP 在PHP中将MySQL日期转换为其他内容

如何在EXCEL中将字符转成日期 如19970828转成1997-08-28

在 MySQL 中将 UTC 格式的日期时间转换为 GMT+7

怎么在Oracle中将时间戳转化为日期格式