Zapytanie można prawdopodobnie uprościć do:
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;
Spowoduje to zwrócenie wszystkich użytkowników, którzy kiedykolwiek mieli jakiekolwiek zadanie.
Plus dane na projekty i dzień gdzie dane istnieją w określonym zakresie dat w task_log_entries
.
Główne punkty
-
funkcja agregująca
sum()
ignorujeNULL
wartości.COALESCE()
za wiersz nie jest już wymagane, gdy tylko przekształcisz obliczenia jako różnicę dwóch sum:,coalesce(sum(nullif(new_value, '')::numeric), 0) - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
Jednak jeśli możliwe, że wszystkie kolumny zaznaczenia mają
NULL
lub puste ciągi, zawiń sumy wCOALESCE
raz.
Używamnumeric
zamiastfloat
, bezpieczniejsza alternatywa, aby zminimalizować błędy zaokrąglania. -
Twoja próba uzyskania różnych wartości z połączenia
users
itasks
jest daremne, ponieważ dołączasz dotasks
jeszcze raz niżej. Spłaszcz całe zapytanie, aby było prostsze i szybsze. -
Te odniesienia pozycyjne to tylko notacyjna wygoda:
GROUP BY 1, 2, 3 ORDER BY 1, 2, 3
... robiąc to samo, co w pierwotnym zapytaniu.
-
Aby uzyskać
date
ztimestamp
możesz po prostu przesłać nadate
:tl.created_on::date AS changeday
Ale znacznie lepiej jest testować z oryginalnymi wartościami w
WHERE
klauzula lubJOIN
warunek (jeśli to możliwe i jest to możliwe tutaj), aby Postgres mógł używać zwykłych indeksów w kolumnie (jeśli są dostępne):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
Pamiętaj, że literał daty jest konwertowany na
timestamp
o00:00
dnia w aktualnym czasie strefa . Musisz wybrać następny dzień i wyklucz to jako górna granica. Lub podaj bardziej wyraźny literał znacznika czasu, taki jak'2013-09-22 0:0 +2':: timestamptz
. Więcej o wykluczeniu górnej granicy: -
Dla wymagania
every user who has ever been assigned to a task
dodajWHERE
klauzula:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
-
Co najważniejsze :
LEFT [OUTER] JOIN
zachowuje wszystkie wiersze na lewo od połączenia. DodawanieWHERE
klauzula po prawej tabela może unieważnić ten efekt. Zamiast tego przenieś wyrażenie filtra doJOIN
klauzula. Więcej wyjaśnień tutaj: -
Nawiasy może służyć do wymuszenia kolejności łączenia tabel. Rzadko potrzebne do prostych zapytań, ale bardzo przydatne w tym przypadku. Korzystam z tej funkcji, aby dołączyć do
tasks
,fixins
,projects
itask_log_entries
przed połączeniem z lewej strony dousers
- bez podzapytania. -
aliasy tabel ułatwiają pisanie złożonych zapytań.