简体   繁体   中英

Oracle SQL comparing two columns in a for loop

I am trying to compare two columns in an SQL table based on two dates. The table is set up so the each person in table A has a foreign key to another table B, each person can have multiple entries in table B. Each entry in table B contains a start date and end date and I am trying to grab each person that has an overlap in table B so the Start Date of one of the entries is before the End Date of any other entry.

So John Doe in table A has two entries in table B where the start day of Entry 1 is April 5th 2015 and end date of April 6th 2016 and the second entry is January 10th 2016 and ends January 10th 2017, so I would want to include this person in my result set.

However Jane Doe in table A as two entries in table B

Entry 1: SD April 10th 2014 End April 10th 2015
Entry 2: SD May 11th 2015 End May 11th 2016

So I would not like to I include Jane Doe in my result set.

I am thinking I need to use two nested for loop in the where part of the select statement flipping a variable back and forth depending on whether or not I want to include this person.

Something along the lines of

Select * from A 
where 
(reset variable
for b in
(select * from b.id = A.b_id); loop
    for btwo in 
    (select * from b.id = A.b_id); loop
    // set variable based on start / end date
    // if I want to include set var = 1 else 0
    end loop;
end loop;)
variable = 1;

You've tagged this question for two database systems - my answer pertains to Oracle.

There's no need to use a loop here as you can use the SQL analytic function LAG to handle this sort of problem.

The following will provide the results you're looking for:

SELECT DISTINCT T.NAME
  FROM (SELECT A.NAME,
               B.START_DATE,
               B.END_DATE,
               LAG(B.START_DATE) OVER (PARTITION BY A.NAME ORDER BY B.NAME, B.START_DATE) AS PREV_START,
               LAG(B.END_DATE) OVER (PARTITION BY A.NAME ORDER BY B.NAME, B.START_DATE) AS PREV_END
          FROM A
          INNER JOIN B
            ON B.NAME = A.NAME
          ORDER BY A.NAME) T
  WHERE (T.START_DATE BETWEEN T.PREV_START AND T.PREV_END) OR
        (T.END_DATE BETWEEN T.PREV_START AND T.PREV_END) OR
        (T.PREV_START BETWEEN T.START_DATE AND T.END_DATE) OR
        (T.PREV_END BETWEEN T.START_DATE AND T.END_DATE);

SQLFiddle here

Here is my suggestion. To get the overlaps you can use:

select b.*
from tableb b
where exists (select 1
              from tableb b2
              where b.aid = b2.aid and
                    b.startdate < b2.enddate and b.enddate > b.startdate
             );

The logic is simple. Two time spans overlaps if the first begins before the second ends, and the first ends after the second begins.

To get the table a values:

with overlaps as (
      select b.*
      from tableb b
      where exists (select 1
                    from tableb b2
                    where b.aid = b2.aid and
                          b.startdate < b2.enddate and b.enddate > b.startdate
                   )
    )
select a.*
from tablea a
where a.aid in (select o.aid from overlaps o);

Note: depending on how you define "overlap" you might want <= and >= for the comparisons.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM