У меня есть таблица журналов, в которой пользователи вводят количество часов, потраченных на проект. (Он используется для графиков и таблиц данных в приложении). Я также создал таблицу "calendar", чтобы я мог оставить даты присоединения и заполнить "пустые" даты нулем. Однако, проведя некоторое время, я не смог найти способ сделать это...
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
Таблица, содержащая все необходимые строки (Calendar
), должна быть слева от LEFT JOIN
, таблица с отсутствующими записями (Logs
) справа. Кроме того, вы можете сохранить порядок таблиц и вместо этого использовать RIGHT JOIN
.
Когда в запросе содержится несколько таблиц, полезно использовать псевдонимы для устранения неоднозначности.
Для полного предложения ON
требуется сравнение.
Диапазон, указанный в состоянии BETWEEN
должен быть в порядке возрастания.
Предложение GROUP BY
требует применения агрегатной функции ко всем негрупповым выражениям в SELECT
-list. Здесь подходит функция SUM
. Поскольку функция SUM
возвращает NULL
если нет совпадающих записей, мы применяем функцию COALESCE
для преобразования их в 0
с.
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;
См. SQL-скрипт: http://sqlfiddle.com/#!9/d28de9c/3/0
Правильное использование LEFT JOIN
. Остальная часть запроса нуждается в очистке:
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';
Заметки:
calendar
, поэтому это должна быть первая таблица в LEFT JOIN
.ON
условия, соединяющих две таблицы.BETWEEN
требует, чтобы операнды были в порядке, поэтому малый до AND
и больше после.GROUP BY
кажется ненужным. COALESCE()
фактически генерирует значение 0
. Ваш порядок соединения неверен, calendar
должен быть слева, так как все его строки должны быть включены независимо от соответствующей строки из logs
. Чтобы преобразовать NULL
в 0
используйте coalesce()
. И если вы GROUP BY
по дате, вы должны применить функцию агрегации на hoursspent
. Думаю, вам нужна 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;
Здесь вы также можете опустить coalesce()
поскольку MySQL sum()
обрабатывает NULL
как 0
любом случае.
Но в образцовых данных предполагается, что вам может вообще не понадобиться GROUP BY
, если в течение одного дня имеется не более одной записи журнала.
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';
logs LEFT JOIN calendar
вместоcalendar LEFT JOIN logs
.