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()ignorerarNULLvä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 hoursMen om det är möjligt att alla kolumner i ett urval har
NULLeller tomma strängar, slå in summorna iCOALESCEen gång.
Jag användernumericistället förfloat, säkrare alternativ för att minimera avrundningsfel. -
Ditt försök att få distinkta värden från sammanfogningen av
usersochtasksä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
datefrån entimestampdu kan helt enkelt casta tilldate:tl.created_on::date AS changedayMen det är mycket bättre att testa med originalvärden i
WHEREklausul ellerJOINskick (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 borderObservera att en datum bokstavlig konverteras till en
timestampvid00:00fö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 tasklägg tillWHEREklausul:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id) -
Det viktigaste :En
LEFT [OUTER] JOINbevarar alla rader till vänster om sammanfogningen. Lägga till enWHEREklausul till höger tabell kan ogiltigförklara denna effekt. Flytta istället filteruttrycket tillJOINklausul. 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,projectsochtask_log_entriesinnan du lämnade sammanfogning med allt tillusers- utan underfråga. -
Tabellalias gör det lättare att skriva komplexa frågor.