Frågan kan förmodligen förenklas till:
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;
Detta returnerar alla användare som någonsin har haft någon uppgift.
Plus data per projekt och dag där data finns inom det angivna datumintervallet i task_log_entries
.
Huvudpunkter
-
aggregationsfunktionen
sum()
ignorerarNULL
värden.COALESCE()
per rad krävs inte längre så snart du omarbetar beräkningen som skillnaden mellan två summor:,coalesce(sum(nullif(new_value, '')::numeric), 0) - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
Men om det är möjligt att alla kolumner i ett urval har
NULL
eller tomma strängar, slå in summorna iCOALESCE
en gång.
Jag användernumeric
istället förfloat
, säkrare alternativ för att minimera avrundningsfel. -
Ditt försök att få distinkta värden från sammanfogningen av
users
ochtasks
är meningslöst eftersom du går med itask
ännu en gång längre ner. Platta ut hela frågan för att göra det enklare och snabbare. -
Dessa positionsreferenser är bara en notationsbekvämlighet:
GROUP BY 1, 2, 3 ORDER BY 1, 2, 3
... gör samma sak som i din ursprungliga fråga.
-
För att få ett
date
från entimestamp
du kan helt enkelt casta tilldate
:tl.created_on::date AS changeday
Men det är mycket bättre att testa med originalvärden i
WHERE
klausul ellerJOIN
skick (om möjligt, och det är möjligt här), så Postgres kan använda vanliga index på kolumnen (om tillgängligt):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
Observera att en datum bokstavlig konverteras till en
timestamp
vid00:00
för dagen vid din nuvarande tid zon . Du måste välja nästa dag och uteslut det som övre kant. Eller ange en mer explicit tidsstämpel som'2013-09-22 0:0 +2':: timestamptz
. Mer om att exkludera övre kant: -
För kravet
every user who has ever been assigned to a task
lägg tillWHERE
klausul:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
-
Det viktigaste :En
LEFT [OUTER] JOIN
bevarar alla rader till vänster om sammanfogningen. Lägga till enWHERE
klausul till höger tabell kan ogiltigförklara denna effekt. Flytta istället filteruttrycket tillJOIN
klausul. Mer förklaring här: -
Parentes kan användas för att tvinga fram ordningen i vilken tabeller sammanfogas. Behövs sällan för enkla frågor, men mycket användbart i det här fallet. Jag använder funktionen för att gå med i
task
,fixins
,projects
ochtask_log_entries
innan du lämnade sammanfogning med allt tillusers
- utan underfråga. -
Tabellalias gör det lättare att skriva komplexa frågor.