Comparing Dates in a MYSQL Subquery - mysql

I have two tables
class
-------------
id name
-------------
1 Knives
2 Pastries
class_date
-------------
get_id start_date
-------------
1 2017-10-09
1 2017-11-15
1 2017-12-03
2 2017-10-30
The class 'Knives' is a series with multiple dates. The class 'Pastries' is only offered on one date.
I want my result to be based on Oct 10, 2017 (or current date). In my search I only want results based on the first date - in this case the date of Oct 9, 2017 for 'Knives' should disqualify it from showing up in the results. 'Pastries' should show up.
I am not sure if I should do a LEFT OUTER JOIN or a Subquery. I've tried both but neither works - but I'm probably not doing it correctly.
This is what I tried:
SELECT *
FROM class, class_date WHERE
class_date.get_id = class.id &&
(SELECT DATE(start_date)
FROM class, class_date WHERE
class_date.get_id = classes.id
ORDER BY class_date.start_date ASC
LIMIT 1
) > CURDATE()
ORDER BY class_date.start_date ASC
and
SELECT *
FROM class
LEFT OUTER JOIN
class_date ON
class_date.get_id = classes.id
WHERE
class_date.start_date > CURDATE()
GROUP BY classes.class_id
ORDER BY class_dates.start_date ASC
I have a feeling that the subquery is the way to go but I get no results. If I use < instead of > I get too many results. Any help would be appreciated.

Here is one method to get the most recent record as of a particular date. This allows you to get all the rows (and you can join in class to get rows there):
select cd.*
from class_date cd
where cd.date = (select max(cd2.date)
from class_date cd2
where cd2.get_id = cd.get_id and
cd2.date <= '2017-10-09'
);
If you just want the maximum date for a given class:
select cd.get_id, max(cd.date)
from class_date cd
where cd.date <= '2017-10-09'
group by cd.get_id;

Related

MySQL most price change over time

price | date | product_id
100 | 2020-09-21 | 1
400 | 2020-09-20 | 2
300 | 2020-09-20 | 3
200 | 2020-09-19 | 1
400 | 2020-09-18 | 2
I add an entry into this table every day with a product's price that day.
Now I want to get most price drops for the last week (all dates up to 2020-09-14), in this example it would only return the product_id = 1, because that's the only thing that changed.
I think I have to join the table to itself, but I'm not getting it to work.
Here's something that I wanted to return the most price changes over the last day, however it's not working.
select pt.price, pt.date, pt.product_id, (pt.price - py.price) as change
from prices as pt
inner join (
select *
from prices
where date > '2020-09-20 19:33:43'
) as py
on pt.product_id = py.product_id
where pt.price - py.price > 0
order by change
I understand that you want to count how many times the price of each product changed over the last 7 days.
A naive approach would use aggregation and count(distinct price) - but it fails when a product's price changes back and forth.
A safer approach is window functions: you can use lag() to retrieve the previous price, and compare it against the current price; it is then easy to aggregate and count the price changes:
select product_id, sum(price <> lag_price) cnt_price_changes
from (
select t.*, lag(price) over(partition by product_id order by date) lag_price
from mytable t
where date >= current_date - interval 7 day
) t
group by product_id
order by price_changes desc
Try using MAX() and MIN() instead....
select MAX(pt.price), MIN(pt.price), MAX(pt.price) - MIN(pt.price) as change
from prices as pt
inner join (
select *
from prices
where date > '2020-09-20 19:33:43'
) as py
on pt.product_id = py.product_id
order by change
Instead of subtracting every row by every other row to get the result, you can find the max and min's easily by means of MAX() and MIN(), and, ultimately, **MAX() - MIN()**. Relevant lines from the linked MySQL documentation...
MAX(): Returns the maximum value of expr.
MIN(): Returns the minimum value of expr.
You won't be able to pull the other fields (id's, dates) since this is a GROUP BY() implied by the MAX() and MIN(), but you should then be able to get that info by query SELECT * FROM ... WHERE price = MAX_VALUE_JUST_ACQUIRED.
This examples will get you results per WeekOfYear and WeekOfMonth regarding the lowering of the price per product.
SELECT
COUNT(m1.product_id) as total,
m1.product_id,
WEEK(m1.ddate) AS weekofyear
FROM mytest m1
WHERE m1.price < (SELECT m2.price FROM mytest m2 WHERE m2.ddate<m1.ddate AND m1.product_id=m2.product_id LIMIT 0,1)
GROUP BY weekofyear,m1.product_id
ORDER BY weekofyear;
SELECT
COUNT(m1.product_id) as total,
m1.product_id,
FLOOR((DAYOFMONTH(ddate) - 1) / 7) + 1 AS weekofmonth
FROM mytest m1
WHERE m1.price < (SELECT m2.price FROM mytest m2 WHERE m2.ddate<m1.ddate AND m1.product_id=m2.product_id LIMIT 0,1)
GROUP BY weekofmonth,m1.product_id
ORDER BY weekofmonth;
Try this out in SQLFiddle.

Joining two tables by date MySQL

I have this:
SELECT * FROM history JOIN value WHERE history.the_date >= value.the_date
is it possible to somehow to ask this question like, where history.the_date is bigger then or equal to biggest possible value of value.the_date?
HISTORY
the_date amount
2014-02-27 200
2015-02-26 2000
VALUE
the_date interest
2010-02-10 2
2015-01-01 3
I need to pair the correct interest with the amount!
So value.the_date is the date since when the interest is valid. Interest 2 was valid from 2010-02-10 till 2014-12-31, because since 2015-01-01 the new interest 3 applies.
To get the current interest for a date you'd use a subquery where you select all interest records with a valid-from date up to then and only keep the latest:
select
the_date,
amount,
(
select v.interest
from value v
where v.the_date <= h.the_date
order by v.the_date desc
limit 1
) as interest
from history h;
use join condition after ON not in where clause...
SELECT * FROM history JOIN (select max(value.the_date) as d from value) as x on history.the_date >= x.d
WHERE 1=1
Presumably, you want this:
select h.*
from history h
where h.the_date >= (select max(v.the_date) from value v);

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

Mysql count group by and left join not working as expected

Below is a simple query for a click tracker. I've had a look at a lot of other posts and I'm scratching my head. I cannot get this query to work so that all rows from the calendar table (one calendar day per row) are displayed:
SELECT DATE_FORMAT(calendar_date, '%a %D') AS calendar_date,
count( tracker_id ) as clicks
FROM calendar
LEFT JOIN offer_tracker USING(calendar_id)
WHERE
calendar_month = Month(CURDATE()) AND
calendar_year = Year(CURDATE()) AND ( offer_id = 4 OR offer_id IS NULL )
GROUP BY calendar_date;
It's nearly there but not all rows in the calendar table are returned i.e. there is no Fri 2nd, Tue 6th, Wed 7th etc:
Does anyone have any ideas on where I'm going wrong? Should I be using a subquery?
I guess the offer_id is from the offer_tracker table. When you have an (left) outer join, and you use a field from the right table in a WHERE condition (like your offer_id = 4), the join is actually cancelled and gives same results as an inner join.
The attempt to lift the cancellation (offer_id = 4 OR offer_id IS NULL) does not work as you expect. Any row from offer_tracker with offer_id <> 4 has already passed the LEFT JOIN but is removed because of the WHERE condition. So, no row with Friday 2nd will appear in the results if there is a row with offer_id different than 4 for this date.
Move the offer_id = 4 check to the LEFT JOIN, instead:
SELECT DATE_FORMAT(calendar_date, '%a %D') AS calendar_date
, count( tracker_id ) as clicks
FROM calendar
LEFT JOIN offer_tracker
ON offer_tracker.calendar_id = calendar.calendar_id
AND offer_id = 4
WHERE calendar_month = Month(CURDATE())
AND calendar_year = Year(CURDATE())
GROUP BY calendar_date

Get date when two things appear at the same time (mysql query)

Is there a sql query that can generate the date when 2 things appear together?
I mean, let's say I have a table consists of bus schedule. Then, I have bus A and B. Bus A will operate on 22 May, 24 May, and 25 May while B operates on 22 May, 24 May and 26 May. I want to get the most recent date that 2 buses appear together which is 24 May.
To see those that both buses share:
SELECT t.date
FROM YOUR_TABLE t
WHERE t.bus IN ('A', 'B')
GROUP BY t.date
HAVING COUNT(DISTINCT t.bus) = 2
To see the most recent date that both buses share:
SELECT t.date
FROM YOUR_TABLE t
WHERE t.bus IN ('A', 'B')
GROUP BY t.date
HAVING COUNT(DISTINCT t.bus) = 2
ORDER BY t.date DESC
LIMIT 1
Assuming you have a table named bus_schedule that contains a bus_name and bus_date field, something like this should work:
select bus_schedule_a.bus_date
from bus_schedule bus_schedule_a
inner join bus_schedule bus_schedule_b
on bus_schedule_a.bus_date = bus_schedule_b.bus_date
and bus_schedule_a.bus_name <> bus_schedule_b.bus_name
order by bus_schedule_a.bus_date desc
limit 1