简体   繁体   中英

SQL Null as 0 with Left Join?

Problem

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

Table 1 : Logs

id | hoursSpent | logDate 
1    5            2018-08-05
2    1            2018-08-07
3    3            2018-08-10

Table 2 : Calendar

id         | db_date
20180807     2018-08-07
20180808     2018-08-08
20180809     2018-08-09
201808010    2018-08-10
etc...

Desired Output

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

Code Used So Far (Doesn't work exactly...)

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:

  • You want all the rows from calendar , so it should be the first table in the LEFT JOIN .
  • You need valid 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.
  • In a query with more than one table reference, qualify all column names.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM