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()ignorujeNULLwartoś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 hoursJednak jeśli możliwe, że wszystkie kolumny zaznaczenia mają
NULLlub puste ciągi, zawiń sumy wCOALESCEraz.
Używamnumericzamiastfloat, bezpieczniejsza alternatywa, aby zminimalizować błędy zaokrąglania. -
Twoja próba uzyskania różnych wartości z połączenia
usersitasksjest daremne, ponieważ dołączasz dotasksjeszcze 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ć
dateztimestampmożesz po prostu przesłać nadate:tl.created_on::date AS changedayAle znacznie lepiej jest testować z oryginalnymi wartościami w
WHEREklauzula lubJOINwarunek (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 borderPamiętaj, że literał daty jest konwertowany na
timestampo00:00dnia 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 taskdodajWHEREklauzula:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id) -
Co najważniejsze :
LEFT [OUTER] JOINzachowuje wszystkie wiersze na lewo od połączenia. DodawanieWHEREklauzula po prawej tabela może unieważnić ten efekt. Zamiast tego przenieś wyrażenie filtra doJOINklauzula. 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,projectsitask_log_entriesprzed połączeniem z lewej strony dousers- bez podzapytania. -
aliasy tabel ułatwiają pisanie złożonych zapytań.