I have a logs table where users enter how many hours they spent on a project. (It's used for graphs and data tables on the application). I also generated a "calendar" table so I can left join dates, and fill the "empty" dates with zero. However, after spending some time, I couldn't find a way to do it...
id | hoursSpent | logDate
1 5 2018-08-05
2 1 2018-08-07
3 3 2018-08-10
id | db_date
20180807 2018-08-07
20180808 2018-08-08
20180809 2018-08-09
201808010 2018-08-10
etc...
db_date | hoursSpent
2018-08-05 5
2018-08-06 0
2018-08-07 1
2018-08-08 0
2018-08-09 0
2018-08-10 3
SELECT hoursSpent, db_date
FROM logs LEFT JOIN calendar ON db_date
WHERE db_date BETWEEN '2018-08-10' AND '2018-08-01'
GROUP BY db_date
The use of LEFT JOIN
is correct. The rest of the query needs cleaning up:
SELECT c.db_date, COALESCE(l.hoursSpent, 0) as hoursSpent
FROM calendar c LEFT JOIN
logs l
ON c.db_date = l.logdate
WHERE c.db_date BETWEEN '2018-08-01' AND '2018-08-10';
Notes:
calendar
, so it should be the first table in the LEFT JOIN
. ON
conditions connecting the two tables. BETWEEN
requires the operands to be in order, so the small one is before the AND
and the larger after. GROUP BY
seems unnecessary.
COALESCE()
actually generates the 0
value. Your join order is wrong, calendar
should be on the left side as all of its rows should be included regardless of a matching row from logs
. To transform NULL
into 0
use coalesce()
. And if you GROUP BY
the date, you have to apply an aggregation function on hoursspent
. I guess you wanted sum()
.
SELECT c.db_date,
sum(coalesce(l.hoursspent, 0)) hoursspent
FROM calendar c
LEFT JOIN logs l
ON l.logdate = c.db_date
WHERE c.db_date BETWEEN '2018-08-10'
AND '2018-08-01'
GROUP BY c.db_date;
Here you can also omit the coalesce()
as MySQL's sum()
treats NULL
as 0
anyway.
But the sample data suggests, that you maybe don't need to GROUP BY
at all, if there aren't more than one log entries for the same day, that is.
SELECT c.db_date,
coalesce(l.hoursspent, 0) hoursspent
FROM calendar c
LEFT JOIN logs l
ON l.logdate = c.db_date
WHERE c.db_date BETWEEN '2018-08-10'
AND '2018-08-01';
The table having all the required rows ( Calendar
) must be on the left side of a LEFT JOIN
, the table with missing records ( Logs
) on the right. Alternatively, you could keep the table order and instead use a RIGHT JOIN
.
When you have more than one table in a query, it is useful to use aliases for disambiguation.
The complete ON
clause needs a comparison.
The range specified in a BETWEEN
condition must be in ascending order.
The GROUP BY
clause requires you to apply an aggregate function to all non-grouping expressions in the SELECT
-list. Here the SUM
function is appropriate. Since the SUM
function returns NULL
s if there are no matching records, we apply the COALESCE
function to convert them to 0
s.
SELECT
c.db_date, COALESCE(SUM(l.hoursSpent), 0) AS hoursSpent
FROM
Calendar c
LEFT JOIN Logs l
ON c.db_date = l.logDate
WHERE
c.db_date BETWEEN '2018-08-01' AND '2018-08-10'
GROUP BY
c.db_date;
See SQL Fiddle: http://sqlfiddle.com/#!9/d28de9c/3/0
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.