Reputation: 7430
I have a table with the following data
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
And want to get the following results
PKey Start End Type
==== ===== === ====
01 01/01/2010 14/01/2010 S
03 05/01/2010 06/01/2010 A
Any ideas on where to start? A lot of the reading I've done suggests I need to create entries and for each day and join on matching days, is this the only way?
Upvotes: 57
Views: 144897
Reputation: 167
With respect to others, all the previous answers are not accurate. The only fully accurate way is:
SELECT * FROM YourTable T1
JOIN YourTable T2 ON T1.PKey <> T2.PKey
AND (T1.[Start] BETWEEN T2.[Start] AND T2.[End] OR T1.[End] BETWEEN T2.[Start] AND T2.[End])
Upvotes: 1
Reputation: 1
To solve if the date is overlapping or not:
Table Creation:
create table testing (
id int,
s_date date,
e_date date
);
Loading Data:
INSERT INTO testing ( id,s_date,e_date)
VALUES ('1','1/1/2020','1/31/2020'),
('2', '1/16/2020', '1/26/2020'),
('3', '1/28/2020', '2/6/2020'),
('4', '2/16/2020', '2/26/2020');
Query:
select id, case when sum(Overlap) > 0 then 'True' else 'False' end as overlap
from (
select a.*, b.id as ids, b.e_date, b.s_date
,case when a.s_date < b.e_date then 1 else 0 end as Overlap
from testing a cross join testing b where a.id <> b.id and a.e_date > b.s_date
) group by 1
Upvotes: 0
Reputation: 86
If you are using PostgreSQL, simply use the built-in overlap operator
SELECT (DATE '2021-01-01', DATE '2021-04-09')
OVERLAPS (DATE '2021-01-20', DATE '2021-02-10');
Upvotes: 7
Reputation: 11
I had to do a very similar thing for to stop duplicate holiday being entered into a table. it was in access and written to a temptable on input so had to query it in 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 "
Upvotes: 1
Reputation: 318
Sql='SELECT task_id
, task_start_date
, task_due_date
FROM (wba_task
) WHERE (task_start_date
<="2016-07-13" AND task_due_date
>="2016-07-25") OR (task_due_date
BETWEEN "2016-07-13" and "2016-07-25")';
Codeigniter Query is below.
$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();
get all overlap data form database....
Upvotes: 0
Reputation: 30882
If you already have entries for each day that should work, but if you don't the overhead is significant, and if that query is used often, if will affect performance.
If the data is in this format, you can detect overlaps using simple date arithmetic, because an overlap is simply one interval starting after a given interval, but before the given is finished, something like
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
If you need special handling for interval that are wholly within another interval, or you need to merge intervals, i.e.
PKey Start End Type
==== ===== === ====
01 01/01/2010 20/01/2010 S
02 15/01/2010 31/01/2010 S
yielding
Start End Type
===== === ====
01/01/2010 31/01/2010 S
you will need more complex calculation.
In my experience with this kind of problems, once you get how to do the calculation by hand, it's easy to transfer it into SQL :)
Upvotes: 53
Reputation: 1330
We've all needed this kind of overlapping predicate in our queries for quite some time and I think I've found a really simple solution here.
In my application, as an example, I have policies that have the same Policy Number but maybe the Policy Description changes from one fiscal year to the next. When a user is entering a new record (same Policy Number, different Policy Description), I needed a way to tell if that policy already exists for the specified time range. If the new Policy Effective/Expiration dates overlap with whatever is already in the database, I needed to error out and tell the user why their input was not correct.
To do this, I went with the following predicate statement:
AND @_expiration >= EffectiveDate AND ExpirationDate >= @_effective
Hopefully someone else finds this as useful as I have.
Upvotes: 1
Reputation: 1633
When I needed to compare two time spans in SQL for overlap, here are the four scenarios I could think of:
Here is the OR statement I created to capture these scenarios (in my case 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
)
Upvotes: 14
Reputation: 2788
BTW - If you don't have a unique id , against your dates you can do this is 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
Upvotes: 0
Reputation: 3393
In MySQL you basically need:
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
>
in the second and the third statement can be replaced with >=
to follow includes matching.
This topic is related to the "Allen's Interval Algebra" and there are some more reading on this can be found by those links:
Upvotes: 2
Reputation: 22895
select A.*
from MyTable A
inner join MyTable B
on (B.start <= A.end)
and (B.end >= A.start)
or something like that (assuming dates are not nullable and equal dates count as an overlap).
Upvotes: 3
Reputation: 91316
Perhaps:
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)
Upvotes: 4