Заявката вероятно може да бъде опростена до:
SELECT u.name AS user_name
, p.name AS project_name
, tl.created_on::date AS changeday
, coalesce(sum(nullif(new_value, '')::numeric), 0)
- coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
FROM users u
LEFT JOIN (
tasks t
JOIN fixins f ON f.id = t.fixin_id
JOIN projects p ON p.id = f.project_id
JOIN task_log_entries tl ON tl.task_id = t.id
AND tl.field_id = 18
AND (tl.created_on IS NULL OR
tl.created_on >= '2013-09-08' AND
tl.created_on < '2013-09-09') -- upper border!
) ON t.assignee_id = u.id
WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
GROUP BY 1, 2, 3
ORDER BY 1, 2, 3;
Това връща всички потребители, които някога са имали някаква задача.
Плюс данни за проекти и дни където съществуват данни в посочения период от време в task_log_entries .
Основни точки
-
агрегирана функция
sum()игнорираNULLстойности.COALESCE()на ред вече не се изисква веднага щом преработите изчислението като разлика от две суми:,coalesce(sum(nullif(new_value, '')::numeric), 0) - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hoursВъпреки това, ако възможно е всички колони от селекция имат
NULLили празни низове, увийте сумите вCOALESCEведнъж.
Използвамnumericвместоfloat, по-безопасна алтернатива за минимизиране на грешките при закръгляване. -
Вашият опит да получите отделни стойности от обединяването на
usersиtasksе безсмислено, тъй като се присъединявате къмtaskоще веднъж по-надолу. Изравнете цялата заявка, за да я направите по-проста и по-бърза. -
Тези позиционни препратки са само за удобство при обозначения:
GROUP BY 1, 2, 3 ORDER BY 1, 2, 3... правите същото като в първоначалната си заявка.
-
За да получите
dateотtimestampможете просто да прехвърлите къмdate:tl.created_on::date AS changedayНо е много по-добре да тествате с оригинални стойности в
WHEREклауза илиJOINусловие (ако е възможно и е възможно тук), така че Postgres може да използва обикновени индекси в колоната (ако има):AND (tl.created_on IS NULL OR tl.created_on >= '2013-09-08' AND tl.created_on < '2013-09-09') -- next day as excluded upper borderОбърнете внимание, че литерал за дата се преобразува в
timestampв00:00от деня в текущия ви час зона . Трябва да изберете следващия ден и изключете като горна граница. Или предоставете по-ясен литерал на клеймо като'2013-09-22 0:0 +2':: timestamptz. Повече за изключването на горната граница: -
За изискването
every user who has ever been assigned to a taskдобаветеWHEREклауза:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id) -
Най-важното :
LEFT [OUTER] JOINзапазва всички редове отляво на съединението. Добавяне наWHEREклауза от вдясно таблицата може да анулира този ефект. Вместо това преместете филтърния израз вJOINклауза Повече обяснения тук: -
Скоби може да се използва за налагане на реда, в който се свързват таблиците. Рядко необходим за прости заявки, но много полезен в този случай. Използвам функцията, за да се присъединя към
task,fixins,projectsиtask_log_entriesпреди да го присъедините къмusers- без подзаявка. -
Псевдоними на таблици улесняват писането на сложни заявки.