简体   繁体   中英

Join to table according to date

I have two tables, one is a list of firms, the other is a list of jobs the firms have advertised with deadlines for application and start dates.

Some of the firms will have advertised no jobs, some will only have jobs that are past their deadline dates, some will only have live jobs and others will have past and live applications.

What I want to be able to show as a result of a query is a list of all the firms, with the nearest deadline they have, sorted by that deadline. So the result might look something like this (if today was 2015-01-01).

Sorry, I misstated that. What I want to be able to do is find the next future deadline, and if there is no future deadline then show the last past deadline. So in the first table below the BillyCo deadline has passed, but the next BuffyCo deadline is shown. In the BillyCo case there are earlier deadlines, but in the BuffyCo case there are both earlier and later deadlines.

id   name     title     date
==   ====     =====     ====
1    BobCo    null      null
2    BillCo   Designer  2014-12-01
3    BuffyCo  Admin     2015-01-31

So, BobCo has no jobs listed at all, BillCo has a deadline that has passed and BuffyCo has a deadline in the future.

The problematic part is that BillCo may have a set of jobs like this:

id   title     date        desired hit
==   =====     ====        ===========
1    Coder     2013-12-01
2    Manager   2014-06-30
3    Designer  2012-12-01  <--

And BuffyCo might have:

id   title     date        desired hit
==   =====     ====        ===========
1    Magician  2013-10-01
2    Teaboy    2014-05-19
3    Admin     2015-01-31  <--
4    Writer    2015-02-28

So, I can do something like:

select * from (
    select * from firms
    left join jobs on firms.id = jobs.firmid
    order by date desc) 
as t1 group by firmid;

Or, limit the jobs joined or returned by a date criterion, but I don't seem to be able to get the records I want returned. ie the above query would return:

id   name     title     date
==   ====     =====     ====
1    BobCo    null      null
2    BillCo   Designer  2014-12-01
3    BuffyCo  Writer    2015-02-28

For BuffyCo it's returning the Writer job rather than the Admin job.

Is it impossible with an SQL query? Any advice appreciated, thanks in advance.

The revision to your question makes it rather trickier, but it can still be done. Try this:

select 
  closest_job.*, firm.name
from
  firms
  left join (
    select future_job.*
    from
      (
        select firmid, min(date) as mindate
        from jobs
        where date >= curdate()
        group by firmid
      ) future
      inner join jobs future_job
        on future_job.firmid = future.firmid and future_job.date = future.mindate

    union all

    select past_job.*
    from
      (
        select firmid, max(date) as maxdate
        from jobs
        group by firmid
        having max(date) < curdate()
      ) past
      inner join jobs past_job
        on past_job.firmid = past.firmid and past_job.date = past.maxdate
  ) closest_job
    on firms.id = closest_job.firmid

I think this may be what you need, you need:

1) calculate the delta for all of your jobs between the date and the  current date finding the min delta for each firm.
2) join firms to jobs only on where firm id's match and where the calculated min delta for the firm matches the delta for the row in jobs.

    SELECT f.id, f.name, j.title,j.date
    FROM firms f LEFT JOIN 
        (SELECT firmid,MIN(abs(datediff(date, curdate())))) AS delta 
         FROM jobs 
         GROUP BY firmid) d 
    ON f.id = d.firmid 
    LEFT JOIN jobs j ON f.id = j.id AND d.delta = abs(datediff(j.date, curdate())))) ;

You want to make an outer join with something akin to the group-wise maximum of (next upcoming, last expired) :

SELECT * FROM firms LEFT JOIN (

  -- fetch the "groupwise" record
  jobs NATURAL JOIN (
    -- using the relevant date for each firm
    SELECT   firmid, MAX(closest_date) date
    FROM     (

               -- next upcoming deadline
               SELECT   firmid, MIN(date) closest_date
               FROM     jobs
               WHERE    date >= CURRENT_DATE
               GROUP BY firmid

             UNION ALL

               -- most recent expired deadline
               SELECT   firmid, MAX(date)
               FROM     jobs
               WHERE    date <  CURRENT_DATE
               GROUP BY firmid

             ) closest_dates
    GROUP BY firmid
  ) selected_dates

) ON jobs.firmid = firms.id

This will actually give you all jobs that have the best deadline date for each firm. If you want to restrict the results to an indeterminate record from each such group, you can add GROUP BY firms.id to the very end.

I think this does what I need:

select * from (
    select firms.name, t2.closest_date from firms
    left join
           (
           select * from (
           --get first date in the future
           SELECT   firmid, MIN(date) closest_date
           FROM     jobs
           WHERE    date >= CURRENT_DATE
           GROUP BY firmid

         UNION ALL

           -- most recent expired deadline
           SELECT   firmid, MAX(date)
           FROM     jobs
           WHERE    date <  CURRENT_DATE
           GROUP BY firmid) as t1

        -- order so latest date is first
        order by closest_date desc) as t2 
     on firms.id = t2.firmid

     -- group by eliminates all but latest date
     group by firms.id) as t3
order by closest_date asc;

Thanks for all the help on this

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