从 Sql 中的表中选择不同的日期间隔

Posted

技术标签:

【中文标题】从 Sql 中的表中选择不同的日期间隔【英文标题】:Select the distinct date intervals From Table in Sql 【发布时间】:2015-04-28 18:56:52 【问题描述】:

包含 From Date 和 To Date 的表格。查询应该删除 FromDate 和 ToDate 在已存在行的区间内的那些行。

表格

    bookingID   FromDate                  ToDate
    15         2015-04-29 17:00:00.000    2015-04-29 18:00:00.000
    13         2015-05-01 10:00:00.000    2015-05-01 14:00:00.000
    14         2015-05-01 13:00:00.000    2015-05-01 14:00:00.000

结果

bookingID   FromDate                  ToDate
13         2015-05-01 10:00:00.000    2015-05-01 14:00:00.000
15         2015-04-29 17:00:00.000    2015-04-29 18:00:00.000

截至日期 2015 年 5 月 1 日,第 1 行涵盖从上午 10 点到下午 2 点的时间。时间为下午 1 点到 2 点的第 2 行已被第 1 行覆盖。

编辑

为了更清楚地说明这种情况,表中不能有更多的两个区间,即 intreval B 中的 A 是唯一的复杂度级别。 所以如果A在intreval B中,那么B应该被删除

【问题讨论】:

这是真正应该用 php 或 python 之类的脚本语言来完成的事情 @TobyAllen:感谢您的建议,我在想是否可以在 SQL 中轻松实现 SQL 并不是真正合适的编程语言,这里的逻辑相当复杂,所以我认为它需要一个简单的脚本或一个极其复杂的存储过程。 您是要实际删除行(您说“删除行”)还是要执行不会选择这些行的查询? 我不同意@Toby。这非常适合 SQL,甚至可能比其他选项更好。 【参考方案1】:

我认为您可以使用 EXISTS 子查询来完成此操作...

SELECT * FROM table a
WHERE NOT EXISTS 
   (SELECT * FROM table b 
    WHERE b.FromDate <= a.FromDate 
    AND b.ToDate >= a.ToDate 
    AND a.bookingID != b.bookingID )

我不确定你是如何定义“在区间内”的,但这应该会给你正确的想法。

由于我现在了解到您实际上是在尝试从表中删除记录,因此您必须反转逻辑并使其成为 DELETE 查询而不是 SELECT 查询:

DELETE FROM table a
WHERE EXISTS 
   (SELECT * FROM table b 
    WHERE b.FromDate <= a.FromDate 
    AND b.ToDate >= a.ToDate 
    AND a.bookingID != b.bookingID )

为了允许可能有两条记录具有相同的FromDate 和相同的ToDate,我们可以这样保留两条记录:

SELECT * FROM table a
WHERE NOT EXISTS 
   (SELECT * FROM table b 
    WHERE b.FromDate <= a.FromDate 
    AND b.ToDate >= a.ToDate 
    AND a.bookingID != b.bookingID
    AND (b.FromDate != a.FromDate OR b.ToDate != a.ToDate) )

或者只包括这样的记录之一:

SELECT * FROM table a
WHERE NOT EXISTS 
   (SELECT * FROM table b 
    WHERE b.FromDate <= a.FromDate 
    AND b.ToDate >= a.ToDate 
    AND a.bookingID != b.bookingID
    AND (b.FromDate != a.FromDate 
         OR b.ToDate != a.ToDate 
         OR a.bookingID < b.bookingID) )

(我已将其移回 SELECT,因为您可以以非破坏性方式对其进行测试。通过将前 2 个单词更改为 DELETE 并去掉 NOT,可以轻松地将其切换回 DELETE 查询。 )

【讨论】:

我尝试了这个建议,但它也删除了那些与任何其他时间间隔都不匹配的行。对不起“在区间内”,我无法猜出要使用的正确词,但我试图解释结果中“在区间内”的含义。 真的吗?我在这里试过 - sqlfiddle.com/#!9/3efca9/1/0 - 它似乎工作正常...... 如果有两个日期完全相同的预订,那么两者都将被删除。我从该案例不会发生的要求中得出结论,但我想注意警告。 是的,2 条单独记录上相同的起止日期意味着每个记录都“包含”在另一个记录中,因此每个记录都删除另一个记录。如果你不想这样,那么将它们都放在里面相对容易,但很难只留下一个。 或者我想你可以为那个特殊情况指定a.bookingID &lt; b.bookingID【参考方案2】:

这将返回另一个记录中包含的所有记录。

SELECT B.* 
FROM tableName A
LEFT JOIN tableName B
 on A.FromDate<= B.FromDate
and A.ToDate>= B.FromDate
and A.bookingID < B.BookingID

删除..

Delete from TableName where ID not in 
(Select b.ID
    FROM tableName A
    LEFT JOIN tableName B
     on A.FromDate<= B.FromDate
    and A.ToDate>= B.FromDate)
and A.bookingID < B.BookingID

但是,如果您还需要以某种方式指示和处理 OVERLAP,而不仅仅是完全包含,那就不同了。因此,清楚地了解您要完成的工作会有所帮助。

你是吗

    想要简单地消除完全包含在另一个中的预订? 标记重叠的预订? 识别其他人中包含的预订?

您给我们的是您要解决的技术问题,但如果没有上下文和用例,您将得到您所要求的,这可能不是您要解决的问题是这是XY problem?

【讨论】:

所有这样的解决方案都以一种复杂度工作,但是如果A in intreval B, and B in interval C, and C in interval D 将只返回 D 记录,结果会怎样? @xQbert :感谢您的回答,想要简单地消除完全包含在另一个中的预订 @xQbert :我尝试了建议的解决方案。但没有运气:( 当我认为您实际上是指 &lt;&gt;!= 时,您已指定 A.bookingID &lt; B.BookingID - 无法保证哪个 ID 会更大或更小,您唯一关心的是他们不相等。

以上是关于从 Sql 中的表中选择不同的日期间隔的主要内容,如果未能解决你的问题,请参考以下文章

如何查找两个不同日期字段SQL在一定时间间隔内是不是匹配?

日期间隔 SQL

如何按期间间隔选择/排序日期?

sql 从2个不同的表中选择唯一的电子邮件,并按日期排序。

用于显示多个日期范围之间的间隔的 SQL 查询

SQL查询中按日期、天间隔分组