检测同一个表中的重叠日期范围

时间:2010-12-20 14:31:47

标签: sql

我有一张包含以下数据的表

PKey  Start       End         Type
====  =====       ===         ====
01    01/01/2010  14/01/2010  S
02    15/01/2010  31/01/2010  S
03    05/01/2010  06/01/2010  A

想获得以下结果

PKey  Start       End         Type
====  =====       ===         ====
01    01/01/2010  14/01/2010  S
03    05/01/2010  06/01/2010  A

关于从哪里开始的任何想法?我所做的很多阅读建议我需要创建条目,并且每天都要加入匹配日,这是唯一的方法吗?

10 个答案:

答案 0 :(得分:36)

如果您已经有每天应该有效的条目,但是如果您没有这样的开销很重要,并且如果经常使用该查询,则会影响性能。

如果数据采用这种格式,您可以使用简单的日期算法检测重叠,因为重叠只是在给定间隔后开始但在给定完成之前的一个间隔,如

select dr1.* from date_ranges dr1
inner join date_ranges dr2
on dr2.start > dr1.start -- start after dr1 is started
  and dr2.start < dr1.end -- start before dr1 is finished

如果你需要特别处理完全在另一个区间内的区间,或者你需要合并区间,即

PKey  Start       End         Type
====  =====       ===         ====
01    01/01/2010  20/01/2010  S
02    15/01/2010  31/01/2010  S

产生

Start       End         Type
=====       ===         ====
01/01/2010  31/01/2010  S

你需要更复杂的计算。

根据我对这类问题的经验,一旦你掌握了如何进行计算,就很容易将其转移到SQL中:)

答案 1 :(得分:4)

当我需要比较SQL中的两个时间跨度以进行重叠时,以下是我能想到的四种情况:

  1. Span1 start在Span2 start和Span2 end
  2. 之间
  3. Span1 end位于Span2 start和Span2 end
  4. 之间
  5. Span1开始和结束都在Span2 start和Span2 end
  6. 之间
  7. Span2开始和结束都在Span1 start和Span1 end
  8. 之间

    这是我为捕获这些场景而创建的OR语句(在我的例子中是Oracle SQL):

    and (
        s1.start between s2.start and s2.end
        OR
        s1.end between s2.start and s2.end
        OR
        s2.start between s1.start and s1.end
    )
    

答案 2 :(得分:2)

select A.*
from MyTable A
inner join MyTable B
on (B.start <= A.end)
and (B.end >= A.start)

或类似的东西(假设日期不可为空,相等的日期算作重叠)。

答案 3 :(得分:2)

也许:

SELECT A.PKey, A.Start, A.End, A.Type
FROM calendar AS A, calendar AS B
WHERE (p.pkey<>a.pkey
AND b.start>=a.start
AND b.end<=a.end)
OR (b.pkey<>a.pkey
AND b.start<=a.start
AND b.end>=a.end)

答案 4 :(得分:1)

我必须做一个非常类似的事情,以阻止重复的假期被输入表格。它在访问中并写入输入的temptable,因此必须在VBA SQL中查询它:

 stCommandText = "SELECT " _
                    & "* " _
                    & "FROM " _
                    & "TableName a, " _
                    & "TableName b " _
                    & "WHERE " _
                    & "a.ID = b.ID " _
                    & "AND a.Startdate >= b.Startdate AND a.StartDate <= b.EndDate " _
                    & "AND a.AutoNo <> b.AutoNo "

答案 5 :(得分:1)

在我们的查询中,我们都需要这种重叠谓词已经有一段时间了,我想我找到了一个非常简单的解决方案here

在我的应用程序中,作为一个例子,我的政策具有相同的政策编号,但政策说明可能会从一个会计年度更改为下一个会计年度。当用户输入新记录(相同的策略号,不同的策略描述)时,我需要一种方法来判断该策略是否已存在指定的时间范围。如果新的策略生效/到期日期与数据库中已有的任何内容重叠,我需要将错误输出并告诉用户他们的输入不正确的原因。

为此,我使用了以下谓词:

AND @_expiration >= EffectiveDate AND ExpirationDate >= @_effective

希望其他人认为这和我一样有用。

答案 6 :(得分:0)

在MySQL中你基本上需要:

SELECT COUNT(*) FROM date_ranges AS A, date_ranges AS B WHERE A.id <> B.id AND A.id > B.id AND A.end_at > B.start_at AND B.end_at > A.start_at

第二个和第三个语句中的

>可以替换为>=以跟随包含匹配。

这个主题与“艾伦的区间代数”有关,关于这方面的更多内容可以通过这些链接找到:

答案 7 :(得分:0)

顺便说一句 - 如果您没有唯一的ID,那么您可以这样做的日期是oracle..FYI

with date_ranges
as
(
SELECT 
     rownum as pkey,
    date_ranges.*
FROM  date_ranges
) 
select 
dr1.* 
from 
date_ranges dr1 , date_ranges dr2
where  dr1.pkey > dr2.pkey
AND dr1.end_dt >= dr2.start_dt 
AND dr2.end_dt >= dr1.start_dt

答案 8 :(得分:0)

Sql ='SELECT task_idtask_start_datetask_due_date FROM(wba_task)WHERE(task_start_date&lt; =“2016-07-13”AND task_due_date&gt; =“2016-07-25”)或(task_due_date BETWEEN“2016-07-13”和“2016-07-25”)';

Codeigniter查询如下。

$fromdaysDate="2016-07-13";//changed date
$todaysDate="2016-07-25";//changed date
$this->db->select('task_id,task_start_date, task_due_date'); 
$this->db->where('task_start_date <="'.date('Y-m-d', strtotime($fromdaysDate)).'"');
$this->db->where('task_due_date >="'.date('Y-m-d', strtotime($todaysDate)).'"');    
$this->db->or_where('task_due_date BETWEEN "'. date('Y-m-d', strtotime($fromdaysDate)). '" and "'. date('Y-m-d', strtotime($todaysDate)).'"');   
$alltask=$this->db->get('wba_task')->result_array();
echo $this->db->last_query();

从数据库中获取所有重叠数据.... enter image description here

答案 9 :(得分:0)

如果您使用的是 PostgreSQL,只需使用内置的 overlap 运算符

SELECT (DATE '2021-01-01', DATE '2021-04-09') 
OVERLAPS (DATE '2021-01-20', DATE '2021-02-10');