Join to table according to date - mysql

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.

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.

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 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

Related

How to combine 2 queries using dynamic dates in mysql?

I have a list of workers and would like to understand how many and what % of workers each day were returning workers. I'm defining a returner as someone that worked anytime from the beginning of the year to the day before date in the specific row. The output I'm looking for:
Date, # of workers total today, # of returners, % returners
7/29 , 5, 3, 60%
7/28 , 10, 5, 50%
and so on...
For 7/29, 3 workers worked on or before 7/28 (day before).
For 7/28, 5 workers worked on or before 7/27 (day before).
I currently have a list of workers that came in for today and a list of workers that came in for all days before today, but I'm not sure how to put them together in one query.Currently the dates are hardcoded; but how do I run this query for each day in the last 2 months? I'm also not sure that the way I'm using dates is correct; is there a way better way other than using BETWEEN?
What I currently have:
List of workers for 1 day (the date should be dynamic depending on the date of the row):
select
/* date_format(CONVERT_TZ(backend_shiftgroup.ends_at,'+00:00','{UTC_OFFSET.RAW}:00'), '%X-W%v') as Week, */
backend_userprofile.name,
business.name as 'Business',
worker_id,
ends_at
from backend_shift
join backend_shiftgroup on backend_shift.shift_group_id = backend_shiftgroup.id
join backend_gigtemplate on backend_shift.gig_id = backend_gigtemplate.id
join business on backend_gigtemplate.business_id = business.id
join backend_company on backend_company.id = backend_gigtemplate.company_id
join backend_userprofile on backend_shift.worker_id = backend_userprofile.id
where is_cancelled = 0
and worker_id is not null
and ends_at BETWEEN '2020-07-28' and '2020-07-29'
group by worker_id
order by count(backend_shift.id) desc;
List of all workers before today:
select
/* date_format(CONVERT_TZ(backend_shiftgroup.ends_at,'+00:00','{UTC_OFFSET.RAW}:00'), '%X-W%v') as Week, */
count(backend_shift.id) as '# of shifts',
backend_userprofile.name,
business.name as 'Business',
worker_id,
ends_at
from backend_shift
join backend_shiftgroup on backend_shift.shift_group_id = backend_shiftgroup.id
join backend_gigtemplate on backend_shift.gig_id = backend_gigtemplate.id
join business on backend_gigtemplate.business_id = business.id
join backend_company on backend_company.id = backend_gigtemplate.company_id
join backend_userprofile on backend_shift.worker_id = backend_userprofile.id
where is_cancelled = 0
and worker_id is not null
and ends_at <= '2020-07-28'
group by worker_id
order by count(backend_shift.id) desc;

How to get days missed in attendance table in mysql using NOT IN

I have a database for taking attendance daily.
every day is accounted for in a datetime (attended) field since there are never no students.
So, for a particular student ID (sid) I figure I'd be able to fill in the gaps of attendance using NOT IN.
select arrived, date_format(arrived,'%a') as 'day'
from attendance
where sid = '38'
and date_format(arrived, '%Y-%m-%d') NOT IN (
select DISTINCT date_format(arrived, '%Y-%m-%d')
from attendance
where MONTH(arrived) = 6
)
and MONTH(arrived) = 6
However, this generates the same results as if I hadn't used NOT IN at all. Now I'm realizing that those missing dates can't print because they're not in the sid limited query to begin with.
Can I move the NOT IN to the front of the query, so that the actual dates I want to display as absent are displayable.
For example, the student was absent 2019-06-06 (since that date is missing in 'his' query and other students were (so the nested query does show 2019-06-06)
See this fiddle: https://www.db-fiddle.com/f/x8VVvMBmizmsbJRHmY1GEd/0
select date(absences) absences from (select distinct date(arrived) absences from attendance) t2
where date(t2.absences) not in (select date(arrived) from attendance where sid=38)
You need to swap the inner and the outer query. You should look for attendance days of all students that are NOT IN attendance days of the specific student (sid=38):
select DISTINCT date(arrived) as 'day'
from attendance
where date(arrived) NOT IN (
select date(arrived)
from attendance
where sid = '38'
and MONTH(arrived) = 6
)
and MONTH(arrived) = 6

MySQL - get the nearest date and show it for every row in table

I have 2 tables - Equipments and Reservations.
Equipment table looks like this:
ID NAME
1 termo
2 hydro
3 force
4 hammer
Reservation table:
ID EQUIPMENT_ID START END
1 2 25.3.2015 26.3.2015
2 2 26.3.2015 27.3.2015
3 1 28.3.2015 29.3.2015
4 3 27.3.2015 28.3.2015
I want to be able to print all equipments and the nearest date in the future from the current date for each equipment.
So the output looks like this when the date is (24.3.2015):
NAME START END
termo 28.3.2015 29.3.2015
hydro 25.3.2015 26.3.2015
force 27.3.2015 28.3.2015
hammer null null
I tried some nested queries, not functioning. To find the nearest future date from the current date is that i know
SELECT start FROM reservations WHERE start > NOW() ORDER BY start
LIMIT 1
but i dont know how to join the tables to find the nearest date for each equipment. Any ideas?
I think a correlated subquery is the easiest way to do this. However, you want three columns from the reservation table, so let's get the id and join in for the rest:
select e.*,
(select r.id
from reservations r
where r.equipment_id = e.id and r.start > now()
order by r.start asc
limit 1
) as next_resid
from equipment e;
The join then requires a subquery:
select e.name, r.start, r.end
from (select e.*,
(select r.id
from reservations r
where r.equipment_id = e.id and r.start > now()
order by r.start asc
limit 1
) as next_resid
from equipment e
) er join
reservations r
on er.next_resid = r.id;
Note that end is not a good name for a column. Although it is not a MySQL reserved word, it is a SQL keyword.

mysql trying a select with multiple conditions

I have 2 tables with values like below:
tbl_users
user_ID name
1 somename1
2 somename2
3 somename3
tbl_interviews
int_ID user_ID answer date
1 1 sometextaba 2012-11-04
2 2 sometextxcec 2012-10-05
3 1 sometextabs 2011-06-04
4 3 sometextxcfc 2012-11-04
5 3 sometextxcdn 2012-11-04
how can i ask mysql tell me who is the only user in the table above that was interviewed this year but had also another interview in the previous years? the only one is the user with id = 1 (since he had an interview (the int_id 1) this year, but the first interview was in 2011 (int-id 3). )
unfortunately I'm not able even to select them..
By joining the table against itself, where one side of the join only includes interviews from this year and the other side only includes previous years, the result of the INNER JOIN will be users having both.
Because it doesn't need to rely on any aggregates or subqueries, this method should be extremely efficient. Especially so, if the date column has an index.
SELECT
DISTINCT
thisyear.user_ID,
name
FROM
/* Left side of join retrieces only this year (year=2012) */
tbl_interviews thisyear
/* Right side retrieves year < 2012 */
/* The combined result will elmininate any users who don't exist on both sides of the join */
INNER JOIN tbl_interviews previous_years ON thisyear.user_ID = previous_years.user_ID
/* and JOIN in the user table to get a name */
INNER JOIN tbl_users ON tbl_users.user_ID = thisyear.user_ID
WHERE
YEAR(thisyear.date) = 2012
AND YEAR(previous_years.date) < 2012
Here is a demonstration on SQLFiddle
A simple approach, perhaps less efficient than JOINs
SELECT DISTINCT user_ID
FROM tbl_interviews
WHERE user_ID IN (
SELECT user_ID
FROM tbl_interviews
WHERE date < 2012-01-01
)
AND user_ID IN (
SELECT user_ID
FROM tbl_interviews
WHERE date > 2012-01-01
)
Following gives you the users taking interviews in Current year, only those who also had appeared in some Previous year/s
SELECT Distinct tc.user_ID FROM tbl_interviews tc
INNER JOIN tbl_interviews tp ON tc.user_ID = tp.user_ID
WHERE YEAR(tc.date) = Year(curDate()) AND YEAR(tp.date) < Year(curDate());
SqlFiddle Demo
Here is a version with no joins, and only one subselect.
SELECT user_id
FROM (
SELECT user_id,
MAX(date) AS last_interview,
COUNT(int_id) AS interviews
FROM tbl_interviews
GROUP BY user_id) AS t
WHERE YEAR(last_interview) = 2012 AND interviews > 1
You can group tbl_interviews by user_id to count the number of interviews per user, and then filter for users who have more than one interview (in addition to having an interview this year). There a number of variations on this theme, according to your specific needs, so let me know if needs a tweak.
For example, this should work as well.
SELECT user_id
FROM (
SELECT user_id,
BIT_OR(YEAR(date) = 2012) AS this_year,
BIT_OR(YEAR(date) < 2012) AS other_year
FROM tbl_interviews
GROUP BY user_id) AS t
WHERE this_year AND other_year

mysql get state on particular date

How could I get row from table with dates nearest to some date? If if insert log on February 27th, state will remain the same for some time and no other record will be added. How can I tell which state was on March 8th for instance?
I have two tables: State and History
State: History:
id|name id| date |id_state
1 |works 1 |2010-08-06 |1
2 |broken 2 |2011-05-10 |1
3 |active 3 |2009-27-01 |2
If I draw timeline of records when when were put in database...
2009-08-06 2010-08-06
---|--------------------------------------------|---------------->
'active' 'broken'
So it was active this entire time. I need all rows from History when state was active and date was March 8th 2010. Thank you
Simple query. Considering your mentioned date of 8th March 2010.
select h.id, h.date,h.id_state from history h
left outer join State s on s.id = h.id_state
where
h.date = '2010-03-08' and s.id = 3
You can rephrase the where clause as below according to your need.
where h.date = '2010-03-08' and s.name = 'active'
This may work
SELECT state.id, history.id, name, date
FROM state
JOIN history ON state.id = history.id_state
WHERE date = '2010-08-06'
Simple joining of 2 tables...
Edit:
To retrieve the last closest date to the given date, use this...
SELECT state.id, history.id, name, date
FROM state
JOIN history ON state.id = history.id_state
WHERE date <= '2012-04-10'
ORDER by date DESC
LIMIT 1
You get exactly ONE, but the closest date...
Edit2:
To retrieve the last closest date to the given date, that IS ACTIVE...
SELECT state.id, history.id, name, date
FROM state
JOIN history ON state.id = history.id_state
WHERE date <= '2012-04-10' AND name = 'active'
ORDER by date DESC
LIMIT 1
You get exactly ONE, but the closest date...
To get the last state before a given date (which will give you the state at the given date), use this query:
select * from (
select *
from log_table
where `date` < $1
and name = 'active'
order by `date` desc) x
limit 1
You can add to the where clause as you like to find the most recent row with some particular condition.