Reputation: 51
I'm trying to get the number of records that have the same FKID and are in the same range of Dates (between Start and End) For Example: FK ID has 2 rows that are within the given date range: ID 210 is between 10:15-11:32 and ID 213 is between 10:30 and 11:47 (they are also on the same day). I need a query that will tell me which IDs have the same FKID and are within the same range. I've been working at this for a while but can't figure it out! Thanks!
ID | FKID | Start | End | TotalTime |
---|---|---|---|---|
208 | 40 | 2021-07-29 09:30:00.000 | 2021-07-29 09:30:00.000 | 0.9 |
209 | 37 | 2021-07-29 10:00:00.000 | 2021-07-29 10:00:00.000 | 0.9 |
210 | 39 | 2021-07-29 10:15:00.000 | 2021-07-29 11:32:00.000 | 77.76 |
211 | 40 | 2021-07-29 13:00:00.000 | 2021-07-29 14:17:00.000 | 77.76 |
212 | 34 | 2021-07-29 13:00:00.000 | 2021-07-29 14:28:00.000 | 88.8685714285714 |
213 | 39 | 2021-07-29 10:30:00.000 | 2021-07-29 11:47:00.000 | 77.76 |
Upvotes: 3
Views: 792
Reputation: 69819
We first need to do a self join to get all the rows in the table with the Same FKID
:
SELECT *
FROM T AS t1
INNER JOIN T AS t2
ON t2.FKID = t1.FKID -- SAME FKID
AND t2.ID <> t1.ID -- Different ID;
The condition t2.ID <> t1.ID
will ensure you aren't joining the same row to itself, instead looking for other rows in the same table. This will return all combinations of rows, e.g.
ID1 | ID2 |
---|---|
210 | 213 |
213 | 210 |
If however you only want each tuple once, you can change to t2 > t1.ID
which will return only
ID1 | ID2 |
---|---|
210 | 213 |
Then for overlapping ranges, the verbose version of the logic is:
So in SQL terms this becomes:
CREATE TABLE #T (ID INT, FKID INT, Start DATETIME, [End] DATETIME, TotalTime INT);
INSERT INTO #T(ID, FKID, Start, [End], TotalTime)
VALUES
(208, 40, '2021-07-29 09:30:00', '2021-07-29 09:30:00', 0.9),
(209, 37, '2021-07-29 10:00:00', '2021-07-29 10:00:00', 0.9),
(210, 39, '2021-07-29 10:15:00', '2021-07-29 11:32:00', 77.76),
(211, 40, '2021-07-29 13:00:00', '2021-07-29 14:17:00', 77.76),
(212, 34, '2021-07-29 13:00:00', '2021-07-29 14:28:00', 88.8685714285714),
(213, 39, '2021-07-29 10:30:00', '2021-07-29 11:47:00', 77.76);
SELECT t1.*
FROM #T AS t1
INNER JOIN #T AS t2
ON t2.FKID = t1.FKID -- SAME FKID
AND t2.ID <> t1.ID -- Different ID
WHERE (t2.Start > t1.Start AND t2.Start < t1.[End])
OR (t2.[End] > t1.Start AND t2.[End] < t1.[End]);
We can however simplify this with De Morgan's laws to simply:
SELECT t1.*
FROM #T AS t1
INNER JOIN #T AS t2
ON t2.FKID = t1.FKID -- SAME FKID
AND t2.ID <> t1.ID -- Different ID
WHERE t2.Start < t1.[End]
AND t2.[End] > t1.Start;
In both cases the output is:
ID | FKID | Start | End | TotalTime |
---|---|---|---|---|
210 | 39 | 2021-07-29 10:15:00.000 | 2021-07-29 11:32:00.000 | 77.76 |
213 | 39 | 2021-07-29 10:30:00.000 | 2021-07-29 11:47:00.000 | 77.76 |
If you want to include rows where the start date equals the end date of a previous row, then just change <
to <=
and >
to >=
:
SELECT t1.*
FROM #T AS t1
INNER JOIN #T AS t2
ON t2.FKID = t1.FKID -- SAME FKID
AND t2.ID <> t1.ID -- Different ID
WHERE t2.Start <= t1.[End]
AND t2.[End] >= t1.Start;
Example on db<>fiddle note rows with ID 214 and 215
Upvotes: 3
Reputation: 6465
You can JOIN the table with itself, looking for records of the same FKID whose ranges overlap (StartA <= EndB) and (EndA >= StartB)
This returns the overlaping IDs :
select A.ID as ID1, B.ID as ID2
from MyTable as A
inner join MyTable as B on A.FKID = B.FKID and
A.Start <= B.End and
A.End >= B.Start
While this query just returns the number of overlaping rows.
select count(*) as Overlaps
from MyTable as A
inner join MyTable as B on A.FKID = B.FKID and
A.Start <= B.End and
A.End >= B.Start
Upvotes: 0