I have the following structure:
id | some_foreign_id | date
1 5 2015-09-29 23:14:23
2 5 2015-09-29 14:13:21
3 8 2015-09-28 22:23:12
For the specified some_foreign_id I want to return the count of rows in this table for each day from last 2 weeks. I created this:
SELECT DATE(t.sent_at), COUNT(*)
FROM table t
INNER JOIN sometable st ON st.some_id = t.id
INNER JOIN someOtherTable sot ON sot.someother_id = st.id
WHERE t.sent_at >= DATE_ADD(CURDATE(), INTERVAL -14 DAY)
AND t.some_foreign_id = 5
GROUP BY DATE(t.sent_at);
It shows some results, but:
Doesn't show 0's if the day has 0 records.
Changing interval to -15 changes the count from the last day - don't know why.
How could I do this properly?
To solve 1., you'll need to left join to something like this and use IFNULL()
To solve 2. (or try to), try changing your query to this (I suggest you solve this first):
SELECT DATE(t.sent_at), COUNT(*)
FROM table t
INNER JOIN sometable st ON st.some_id = t.id
INNER JOIN someOtherTable sot ON sot.someother_id = st.id
WHERE DATE(t.sent_at) >= DATE(DATE_ADD(CURDATE(), INTERVAL -14 DAY))
AND t.some_foreign_id = 5
GROUP BY DATE(t.sent_at);
Related
I have a table of grape patches. Each patch has a table of Sprays, which have a date & spray type, each table of spray types have a 'minimum time' til you can pick.
Patch table:
PatchID | earliestDate
1 ---
2 ---
Spray Table
SprayID | PatchID | TypeID |Date
1 1 1 2019-06-1
2 1 2 2019-06-16
1 2 1 2019-06-16
2 2 2 2019-06-1
Spray Type
TypeID | minimumTime
1 14
2 28
I've grabbed out the
MAX(newEarliest) FROM ( SELECT DATE_ADD(Spray.Date, INTERVAL Type.minimumTime DAY) As newEarliest
FROM Spray
LEFT JOIN Patch ON Patch.PatchID = Spray.PatchID
LEFT JOIN Type ON Type.TypeID = Spray.TypeID) WHERE Patch.PatchID = 1;
But from here I'm stuck
UPDATE Patch SET EarliestDate = MAX(newEarliest) FROM ( SELECT DATE_ADD(Spray.Date, INTERVAL Type.minimumTime DAY) As newEarliest
FROM Spray
LEFT JOIN Patch ON Patch.PatchID = Spray.PatchID
LEFT JOIN Type ON Type.TypeID = Spray.TypeID) ) WHERE ??;
The Expected results should be, Patch 1 gets 14th of July, Patch 2 gets 30th of July.. yet I can't make that last connection. I'm hoping/expecting? I've just hit a wall and will need a breather, but maybe there is a SQL command that I've missed?
try this query. you can get the max() group by PatchID first as subquery before doing the update.
update Patch p
Inner join (
select t1.PatchID, max(date_add(t1.Date, interval t3.minimumTime day)) as newEarliest
from Spray t1
left join Patch t2 on t2.PatchID = t1.PatchID
left join Type t3 on t3.TypeID = t1.TypeID
group by t1.PatchID) t on t.PatchID = p.PatchID
set p.EarliestDate = t.newEarliest
I've got a query which produces the proper results for a given time interval of 15 minutes.
-- Query for the interval 10:00-10:15
SELECT count(r.id) as nof_reservations_in_interval
FROM reservations r
LEFT JOIN assets a ON r.asset_id = a.id
WHERE r.deleted_at is null
AND a.type_id = 23 --just an ID
AND r.start_utc <= '2017-02-21 10:15:00'
AND r.end_utc >= '2017-02-21 10:00:00'
-- result: 2
If I want to make a 'table/relation' with the results for this query between, lets say, 10:00 and 18:00 on the same day. How could I achieve that?
I could just query the statement from php for every interval; but I hoped there was some kind of smart MySQL function to do this :)
Desired result relation:
interval_start | interval_end | nof_reservations_in_interval
---------------+--------------+------------------------------
10:00 | 10:15 | 2
10:15 | 10:30 | 3
etc etc
A simple way is to define the intervals using a subquery:
SELECT t.time_start, t.time_end, count(r.id) as nof_reservations_in_interval
FROM (SELECT time('10:10:00') as time_start, time('10:15:00') as time_end UNION ALL
SELECT time('10:15:00') as time_start, time('10:30:00') as time_end
) t LEFT JOIN
reservations r
ON r.start_utc <= addtime('2017-02-21', t.time_end) AND
r.end_utc >= addtime('2017-02-21', t.time_start) LEFT JOIN
assets a
ON r.asset_id = a.id AND
a.type_id = 23
WHERE r.deleted_at is null
GROUP BY t.time_start, t.time_end;
Note: I moved the condition on a.type_id to the on clause for the left joins to work.
I´m trying to fill the gaps after using group by using an aux table, can you help?
aux table to deal with days with no orders
date quantity
2014-01-01 0
2014-01-02 0
2014-01-03 0
2014-01-04 0
2014-01-05 0
2014-01-06 0
2014-01-07 0
group by result from "orders" table
date quantity
2014-01-01 7
2014-01-02 1
2014-01-04 2
2014-01-05 3
desired result joining "orders" table with "aux table"
date quantity
2014-01-01 7
2014-01-02 1
2014-01-03 0
2014-01-04 2
2014-01-05 3
2014-01-06 0
2014-01-07 0
Without knowing how you create your group by result table, what you're looking for in an outer join, perhaps with coalesce. Something like this:
select distinct a.date, coalesce(b.quantity,0) quantity
from aux a
left join yourgroupbyresults b on a.date = b.date
Please note, you may or may not need distinct -- depends on your data.
Edit, given your comments, this should work:
select a.date, count(b.date_sent)
from aux a
left join orders b on a.date = date_format(b.date_sent, '%Y-%m-%d')
group by a.date
SQL Fiddle Demo
Using your results it would be something like:
SELECT a.date
,COALESCE(b.quantity,0) as quantity
FROM auxtable a
LEFT JOIN groupbyresult b
ON a.date = b.date
You can also do your grouping in the same query as the left join:
SELECT a.date
,COALESCE(COUNT(b.somefield),0) as quantity
FROM auxtable a
LEFT JOIN table1 b
ON a.date = b.date
GROUP BY a.date
One familiar approach to solving a problem like this is to use a row source that has the distinct list of dates you want to return, and then do an outer join to the table that has gaps. That way, you get all the dates back, and you can substitute a zero for the "missing" quantity values.
For example:
SELECT d.date
, IFNULL(SUM(s.quantity),0) AS quantity
FROM distinct_list_of_dates d
LEFT
JOIN information_source s
ON s.date = d.date
GROUP BY d.date
It's not clear why a GROUP BY would be eliminating some date values. We might conjecture that you are using a MySQL extension to ANSI-standard GROUP BY semantics, and that is eliminating rows. Or, you may have a WHERE clause that is excluding rows. But we're just guessing.
FOLLOW UP based on further information revealed by OP in comments...
In the query above, replace distinct_list_of_dates with aux, and replace information_source with orders, and adjusting the join predicate to account for datetime comparison to date
SELECT d.date
, IFNULL(SUM(s.quantity),0) AS quantity
FROM aux d
LEFT
JOIN orders s
ON s.date >= d.date
AND s.date < d.date + INTERVAL 1 DAY
GROUP BY d.date
I have two queries that get the:
Most recent snow measurement (within 1 hour)
And a measurement taken 24 hours ago.
When I UNION the queries, I am expecting the two measurements to be in separate columns, but they are actually returned in separate (duplicate) rows. See output below.
SELECT snowfall.cms as now_snow, resorts.*, regions.name
FROM snowfall
INNER JOIN resorts on resorts.id = snowfall.resort_id
INNER JOIN regions ON resorts.region_id = regions.id
WHERE snowfall.timestamp >= SUBDATE( NOW( ) , INTERVAL 1 HOUR )
GROUP BY resorts.id
UNION
SELECT snowfall.cms as 24hr_snow, resorts.*, regions.name
FROM snowfall
INNER JOIN resorts on resorts.id = snowfall.resort_id
INNER JOIN regions ON resorts.region_id = regions.id
WHERE snowfall.timestamp >= SUBDATE( NOW() , INTERVAL 1 DAY)
GROUP BY resorts.id
ORDER BY now_snow DESC
I am getting a result of:
now_snow | resorts.name | ...
========================================
20 | The Mountain
15 | The Mountain
18 | The Hill
102 | The Hill
But was expecting a result of:
now_snow | 24hr_snow | resorts.name | ...
========================================
20 | 15 | The Mountain
18 | 102 | The Hill
Is UNION correct in this scenario? How can I achieve the desired output?
A UNION will append rows to your query. In order to broaden your query you need a join.
I would suggest a self join in this case. One snowfall for daily, one for hourly. Both joined on resort id and grouped by resort name.
SELECT sum(hourly.cms) as now_snow, sum(daily.cms) as 24hr_snow, resorts.name
FROM
snowfall daily
INNER JOIN resorts on resorts.id = daily.resort_id
INNER JOIN regions ON resorts.region_id = regions.id
INNER JOIN snowfall hourly on resorts.id = hourly.resort_id
WHERE daily.timestamp >= SUBDATE( NOW() , INTERVAL 1 DAY)
AND hourly.timestamp >= SUBDATE( NOW( ) , INTERVAL 1 HOUR )
GROUP BY resorts.name
ORDER BY now_snow DESC
You didnt use an aggregate in your post and mysql may allow for that, but I've added it here for clarity.
How can I update a table after some time interval when a condtion is matched?
tb_contest
id contest_id name is_expire
1 101 new 0
2 102 old 0
tb_answer
contest_id answer_id date
101 1 2012-02-02
101 2 2012-09-14
102 5 2012-06-01
I need to update tb_contest after some condition was met and make is_expire=1 after 2 days on basis of the last answer received i:e 2012-03-14, so the tb_contest should be updated on 2012-09-16.
You could use MySQL's event scheduler:
CREATE EVENT expire_contests
ON SCHEDULE EVERY DAY
STARTS CURRENT_DATE
DO UPDATE tb_contest JOIN (
SELECT contest_id, MAX(date) AS latest
FROM tb_answer
GROUP BY contest_id
) t USING (contest_id)
SET tb_contest.is_expire = 1
WHERE tb_contest.is_expire <> 1
AND t.latest <= CURRENT_DATE - INTERVAL 2 DAY
Try this one,
UPDATE tb_contest a INNER JOIN
(
SELECT contest_ID, MAX(`date`) maxDate
FROM tb_answer
GROUP BY contest_ID
) b ON a.contest_ID = b.contest_ID
SET a.is_expire = 1
WHERE DATEDIFF(CURDATE(), b.maxDate) >= 2 AND
a.is_expire = 0
So here it goes, the two tables were joined by contest_ID and having the lastest answered date on tb_answer. By using DATEDIFF() we can know the difference between today's date and the date the contest has been answered.
You can JOIN the contest and an inner-query on the answer table in the UPDATE clause and use MySQL's DATEDIFF to count the number-of-days since the answer was, well, answered:
UPDATE
tb_contest c
JOIN (SELECT contest_id, MAX(date) AS date FROM tb_answer GROUP BY contest_id) AS a
ON a.contest_id = c.id
SET
c.is_expire = 1
WHERE
DATEDIFF(NOW(), a.date) >= 2