SQL Group By Date冲突

Pea*_*nut 8 mysql sql group-by

我有一个包含start_date和end_date列的表.我们需要做的是选择所有内容并按每个Object_ID的日期冲突对它们进行分组.

日期冲突是指行的开始日期和/或结束日期通过另一行'.例如,以下是冲突的一些示例:

第1行的日期为1日至5日,第2行的日期为2日至3日.

第1行的日期是第2到第5,第2行的日期是第1到第3.

第1行的日期是第2到第5,第2行的日期是第3到第6.

第1行的日期是第2到第5,第2行的日期是第1到第7.

例如,如果我们有一些样本数据(假设数字只是一个月中的简单日期):

id | object_id | start_date | end_date
1  | 1         | 1          | 5
2  | 1         | 2          | 4
3  | 1         | 6          | 8
4  | 2         | 2          | 3
Run Code Online (Sandbox Code Playgroud)

我期望看到的是:

object_id | start_date | end_date | numconflicts
1         | <na>       | <na>     | 2
1         | 6          | 8        | 0 or null
2         | 2          | 3        | 0 or null
Run Code Online (Sandbox Code Playgroud)

对于第二个测试用例,以下是一些示例数据:

id | object_id | start_date | end_date
1  | 1         | 1          | 5
2  | 1         | 2          | 4
3  | 1         | 6          | 8
4  | 2         | 2          | 3
5  | 2         | 4          | 5
6  | 1         | 2          | 3
7  | 1         | 10         | 12
8  | 1         | 11         | 13
Run Code Online (Sandbox Code Playgroud)

对于第二个测试用例,我期望看到输出:

object_id | start_date | end_date | numconflicts
1         | <na>       | <na>     | 3
1         | 6          | 8        | 0 or null
2         | 2          | 3        | 0 or null
2         | 4          | 5        | 0 or null
1         | <na>       | <na>     | 2
Run Code Online (Sandbox Code Playgroud)

是的,我需要一些区分第一和第二组的方法(第一行和最后一行),但我还没有想到这一点.目标是查看此列表,然后当您单击一组冲突时,您可以查看该组中的所有冲突.

我的第一个想法是尝试一些GROUP BY CASE ......条款,但我只是围绕着自己.

我用来调用mysql的语言是php.因此,如果有人知道一个php循环解决方案而不是一个大型的mysql查询,那我就是耳朵.

提前致谢.

编辑:在主键中添加,以减少混淆.

编辑:在测试用例2中添加以提供更多推理.

Boh*_*ian 2

此查询查找重复项的数量:

select od1.object_id, od1.start_date, od1.end_date, sum(od2.id is not null) as dups
from object_date od1
left join object_date od2
    on od2.object_id = od1.object_id
    and od2.end_date >= od1.start_date
    and od2.start_date <= od1.end_date
    and od2.id != od1.id
group by 1,2,3;
Run Code Online (Sandbox Code Playgroud)

您可以使用此查询作为查询的基础,该查询可以准确地提供您所要求的内容(请参阅下面的输出)。

select
  object_id,
  case dups when 0 then start_date else '<na>' end as start_date,
  case dups when 0 then end_date else '<na>' end as end_date,
  sum(dups) as dups
from (
  select od1.object_id, od1.start_date, od1.end_date, sum(od2.id is not null) as dups
  from object_date od1
  left join object_date od2
    on od2.object_id = od1.object_id
    and od2.end_date >= od1.start_date
    and od2.start_date <= od1.end_date
    and od2.id != od1.id
  group by 1,2,3) x
group by 1,2,3;
Run Code Online (Sandbox Code Playgroud)

请注意,我使用了id列来区分行。但是,您可以将 id 不匹配的测试替换为每列上的比较,即替换od2.id != od1.id为每个其他列不相等的测试,但这需要所有其他列上的唯一索引才有意义,并且具有 id 列无论如何是个好主意。

这是使用您的数据进行的测试:

create table object_date (
    id int primary key auto_increment,
    object_id int,
    start_date int,
    end_date int
);
insert into object_date (object_id, start_date, end_date) 
    values (1,1,5),(1,2,4),(1,6,8),(2,2,3);
Run Code Online (Sandbox Code Playgroud)

针对此示例数据运行时第一个查询的输出:

+-----------+------------+----------+------+
| object_id | start_date | end_date | dups |
+-----------+------------+----------+------+
|         1 |          1 |        5 |    1 |
|         1 |          2 |        4 |    1 |
|         1 |          6 |        8 |    0 |
|         2 |          2 |        3 |    0 |
+-----------+------------+----------+------+
Run Code Online (Sandbox Code Playgroud)

针对此示例数据运行时第二个查询的输出:

+-----------+------------+----------+------+
| object_id | start_date | end_date | dups |
+-----------+------------+----------+------+
|         1 | 6          | 8        |    0 |
|         1 | <na>       | <na>     |    2 |
|         2 | 2          | 3        |    0 |
+-----------+------------+----------+------+
Run Code Online (Sandbox Code Playgroud)