Заявката вероятно може да бъде опростена до:
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
- без подзаявка. -
Псевдоними на таблици улесняват писането на сложни заявки.