【问题标题】:Filter out rows with date range gaps in mysql过滤掉mysql中日期范围差距的行
【发布时间】:2017-05-04 21:32:47
【问题描述】:

在 mysql 中,我有一个类似于以下的表:

--------------------------------------------
| id | parent_id | date_start | date_end   |
--------------------------------------------
| 1  |           | 2017-05-01 | 2017-05-10 |
| 2  | 1         | 2017-05-01 | 2017-05-10 |
| 3  |           | 2017-06-01 | 2017-06-10 |
| 4  | 3         | 2017-06-01 | 2017-06-03 |
| 5  | 3         | 2017-06-04 | 2017-06-06 |
| 6  | 3         | 2017-06-07 | 2017-06-10 |
| 7  |           | 2017-07-01 | 2017-07-10 |
| 8  | 7         | 2017-07-01 | 2017-07-03 |
| 9  | 7         | 2017-07-04 | 2017-07-06 |
| 10 | 7         | 2017-07-08 | 2017-07-10 |

没有父 ID 的行是“价目表”,而有父 ID 的行是价目表期间。 我想过滤掉具有时间间隔的价目表 ID,所以理想情况下我的查询应该返回 1 和 3。 到目前为止,我已经编写了一个正确返回 3 的简单查询:

SELECT distinct period1.parent_id
FROM pricelist period1
INNER JOIN pricelist period2
ON period1.parent_id = period2.parent_id
AND period2.date_start = DATE_ADD(period1.date_end,INTERVAL 1 DAY);

但不幸的是,它没有考虑具有单个期间的价目表,根据定义,这些价目表没有间隙!

所以我想知道是否可以修改这样的查询以返回具有单个周期或多个周期的价目表而没有时间间隔,可能没有 UNION。

【问题讨论】:

  • 您包含的查询是获得“3”的查询。我做了一个小提琴 sqlfiddle.com/#!9/de7e3/2> 并得到 3 和 7。仍然错了,但这让我想知道缺少什么。
  • 不知道...可能与id和parent_id之间的外键有关

标签: mysql date-range self-join gaps-and-islands


【解决方案1】:

我很难找到 MySQL 工作区,所以最初我提出了一个 T-SQL 解决方案,但后来在 rextester 学习了如何使用 MySQL,这对我有很大帮助。 MySQL 中 DATEDIFF() 函数的语法与 T-SQL 的逻辑相反,后者变得复杂而无法对其进行测试。希望现在解决了。

这种方法的基本逻辑是计算父母的总时长。然后计算所有孩子的持续时间的总和。然后比较这些持续时间(在连接中),如果它们相同,则没有间隙。

请注意,此逻辑未针对子项中的重叠进行测试,但我希望这些逻辑在比较持续时间的连接处也会失败。

数据:

#drop table if exists `PRICELIST`;

create table `PRICELIST`
    (`id` int, `parent_id` int, `date_start` datetime, `date_end` datetime)
;

INSERT INTO PRICELIST
    (`id`, `parent_id`, `date_start`, `date_end`)
VALUES
    (1, NULL, '2017-05-01 00:00:00', '2017-05-10 00:00:00'),
    (2, 1, '2017-05-01 00:00:00', '2017-05-10 00:00:00'),
    (3, NULL, '2017-06-01 00:00:00', '2017-06-10 00:00:00'),
    (4, 3, '2017-06-01 00:00:00', '2017-06-03 00:00:00'),
    (5, 3, '2017-06-04 00:00:00', '2017-06-06 00:00:00'),
    (6, 3, '2017-06-07 00:00:00', '2017-06-10 00:00:00'),
    (7, NULL, '2017-07-01 00:00:00', '2017-07-10 00:00:00'),
    (8, 7, '2017-07-01 00:00:00', '2017-07-03 00:00:00'),
    (9, 7, '2017-07-04 00:00:00', '2017-07-06 00:00:00'),
    (10, 7, '2017-07-08 00:00:00', '2017-07-10 00:00:00')
;

MySQL:

select p.id, datediff(p.date_end,p.date_start) pdu, x.du
from pricelist p
inner join (
        select
        p1.parent_id, sum(datediff(p1.date_end,p1.date_start) + coalesce(datediff(p2.date_start,p1.date_end),0)) du
        from (
            select parent_id,date_start,date_end
            from pricelist
            where parent_id IS NOT NULL
             ) p1
        left join (
            select parent_id,date_start,date_end
            from pricelist
            where parent_id IS NOT NULL
             ) p2 on p1.parent_id = p2.parent_id and p1.date_end < p2.date_start
        where datediff(p2.date_start,p1.date_end) = 1 or p2.parent_id is null
        group by p1.parent_id
    ) x on p.id = x.parent_id and datediff(p.date_end,p.date_start) = x.du
where p.parent_id IS NULL
;

看到它在工作:http://rextester.com/JRD47056

T-SQL:

虽然我找不到有效的 MySQL 环境,但我使用了 MSSQL,但避免了任何不能在 MySQL 中使用的分析函数等。但是我依赖 datediff() ,它与 MySQL 中的相同函数略有不同。

TSQL 查询

select p.id, datediff(day,p.date_start,p.date_end) du
from @pricelist p
inner join (
        select
        p1.parent_id, sum(datediff(day,p1.date_start,p1.date_end) + coalesce(datediff(day,p1.date_end,p2.date_start),0)) du
        from (
            select parent_id,date_start,date_end
            from @pricelist
            where parent_id IS NOT NULL
             ) p1
        left join (
            select parent_id,date_start,date_end
            from @pricelist
            where parent_id IS NOT NULL
             ) p2 on p1.parent_id = p2.parent_id and p1.date_end < p2.date_start
        where datediff(day,p1.date_end,p2.date_start) = 1 or p2.parent_id is null
        group by p1.parent_id
    ) x on p.id = x.parent_id and datediff(day,p.date_start,p.date_end) = x.du
where p.parent_id IS NULL

看到它在工作:http://rextester.com/KUK2410


在 MySQL 中,datediff() 只需要 2 个参数,您需要交换字段引用(即最新日期优先)

也许有更简单的方法。我现在能想到的最好的。

【讨论】:

  • 在 mysql 中它确实返回 1 而它应该返回 1 和 3
  • 你把datediff函数里的参数调反了吗?即 datediff(end,start) :但在 mSsql 中是 datediff(unit,start,end)
  • 我已经添加了一个mysql解决方案。
猜你喜欢
  • 1970-01-01
  • 2014-01-29
  • 1970-01-01
  • 2017-04-17
  • 1970-01-01
  • 2018-02-06
  • 2021-09-06
  • 1970-01-01
  • 2020-02-15
相关资源
最近更新 更多