De zoekopdracht kan waarschijnlijk worden vereenvoudigd tot:
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;
Dit geeft alle gebruikers terug die ooit een taak hebben gehad.
Plus gegevens per projecten en dag waar gegevens bestaan in het opgegeven datumbereik in task_log_entries
.
Belangrijkste punten
-
De aggregaatfunctie
sum()
negeertNULL
waarden.COALESCE()
per rij is niet meer nodig zodra u de berekening herschikt als het verschil van twee sommen:,coalesce(sum(nullif(new_value, '')::numeric), 0) - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
Echter, als het is mogelijk dat alle kolommen van een selectie hebben
NULL
of lege strings, wikkel de sommen inCOALESCE
eenmaal.
Ik gebruiknumeric
in plaats vanfloat
, veiliger alternatief om afrondingsfouten te minimaliseren. -
Uw poging om verschillende waarden te verkrijgen uit de samenvoeging van
users
entasks
is zinloos, aangezien je meedoet aantask
nog een keer verder naar beneden. Maak de hele query plat om het eenvoudiger en sneller te maken. -
Deze positionele referenties zijn slechts een notatie gemak:
GROUP BY 1, 2, 3 ORDER BY 1, 2, 3
... hetzelfde te doen als in uw oorspronkelijke zoekopdracht.
-
Om een
date
te krijgen van eentimestamp
je kunt gewoon casten naardate
:tl.created_on::date AS changeday
Maar het is veel beter om te testen met originele waarden in de
WHERE
clausule ofJOIN
voorwaarde (indien mogelijk, en het is hier mogelijk), zodat Postgres gewone indices op de kolom kan gebruiken (indien beschikbaar):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
Merk op dat een letterlijke datum wordt geconverteerd naar een
timestamp
om00:00
van de dag op jouw huidige tijd zone . Je moet de volgende . kiezen dag en uitsluiten het als bovenrand. Of geef een meer expliciete letterlijke tijdstempel, zoals'2013-09-22 0:0 +2':: timestamptz
. Meer over het uitsluiten van de bovenrand: -
Voor de vereiste
every user who has ever been assigned to a task
voeg deWHERE
. toe clausule:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
-
Het belangrijkste :EEN
LEFT [OUTER] JOIN
behoudt alle rijen links van de join. EenWHERE
toevoegen clausule aan de rechter tabel kan dit effect teniet doen. In plaats daarvan verplaats de filterexpressie naar deJOIN
clausule. Meer uitleg hier: -
Haakjes kan worden gebruikt om de volgorde waarin tabellen worden samengevoegd te forceren. Zelden nodig voor eenvoudige vragen, maar in dit geval erg handig. Ik gebruik de functie om deel te nemen aan
task
,fixins
,projects
entask_log_entries
voordat je alles samenvoegt metusers
- zonder subquery. -
Tabelaliassen maak het schrijven van complexe query's gemakkelijker.