【问题标题】:SQL Group By Date ConflictsSQL 按日期分组冲突
【发布时间】:2011-10-04 03:33:19
【问题描述】:

我有一个包含 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

我希望看到的是:

object_id | start_date | end_date | numconflicts
1         | <na>       | <na>     | 2
1         | 6          | 8        | 0 or null
2         | 2          | 3        | 0 or null

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

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

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

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

是的,我需要一些方法来区分第一组和第二组(第一行和最后一行),但我还没有完全弄清楚。目标是查看此列表,然后当您单击一组冲突时,您可以查看该组中的所有冲突。

我的第一个想法是尝试一些 GROUP BY CASE ... 子句,但我只是把头缠在自己身上。

我用来调用mysql的语言是php。因此,如果有人知道 php-loop 解决方案而不是大型 mysql 查询,我会全神贯注。

提前致谢。

编辑:在主键中添加以减少混乱。

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

【问题讨论】:

  • 不,object_id 不引用表的 id,它只是将对象引用到具有该 id 的不同表。这意味着,只有具有相似 object_id 的行才有可能发生冲突。
  • 只是想一想:如果您打算将冲突行设为可点击和可展开的,那么使用 &lt;na&gt; 而不是实际日期似乎没有多大意义。如果我是你,我可能会使用MIN(start_date) AS start_dateMAX(end_date) AS end_date 作为冲突行。这样用户就可以立即看到冲突组所属的日期范围,而无需先单击/展开它。

标签: mysql sql group-by


【解决方案1】:

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

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;

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

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;

请注意,我使用了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);

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

+-----------+------------+----------+------+
| object_id | start_date | end_date | dups |
+-----------+------------+----------+------+
|         1 |          1 |        5 |    1 |
|         1 |          2 |        4 |    1 |
|         1 |          6 |        8 |    0 |
|         2 |          2 |        3 |    0 |
+-----------+------------+----------+------+

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

+-----------+------------+----------+------+
| object_id | start_date | end_date | dups |
+-----------+------------+----------+------+
|         1 | 6          | 8        |    0 |
|         1 | <na>       | <na>     |    2 |
|         2 | 2          | 3        |    0 |
+-----------+------------+----------+------+

【讨论】:

  • 您的 group by 子句到底在做什么? 1,2,3 是从哪里来的?
  • @Peanut group by 1,2,3group by column1, column2, column3 的简写语法——在本例中为group by od1.object_id, od1.start_date, od1.end_date。它是 SQL 标准的一部分,适用于每个数据库。我发现它很多更容易阅读,而且恕我直言,它较少容易出错,尤其是当被分组的列是计算时 - 许多数据库要求您重复 group by 中的计算,这会导致某种形式的重复,因此是潜在的错误/错误来源
  • 你的回答是得到我需要的东西,但并不完全在那里。我现在将更新线程以解释更多。
【解决方案2】:

Oracle:这可以通过 CASE 语句分组中的子查询来完成。

https://forums.oracle.com/forums/thread.jspa?threadID=2131172

Mysql:你可以有一个包含所有冲突的视图。

从约会 a1 中选择不同的 a1.appt、a2.appt、约会 a2 其中 a1.start a2.start。

然后简单地对该表执行计数(*)。

【讨论】:

    【解决方案3】:

    类似下面的东西应该可以工作:

    select T1.object_id, T1.start_date, T1.end_date, count(T1.object_id) as numconflicts
    from T1
    inner join T2 on T1.start_date between T2.start_date and T2.end_date
    inner join T3 on T1.end_date between T2.start_date and T2.end_date
    group by T1.object_id
    

    我可能有一点不妥,但它应该可以帮助您入门。

    编辑:正确缩进

    【讨论】:

    • 只有 1 个表,但我可以通过说内部连接 ​​(SELECT .... ) 作为 T2 来查看派生 T2 的位置。
    猜你喜欢
    • 1970-01-01
    • 2010-10-17
    • 2014-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-18
    • 2013-12-18
    • 1970-01-01
    相关资源
    最近更新 更多