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()negeertNULLwaarden.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 hoursEchter, als het is mogelijk dat alle kolommen van een selectie hebben
NULLof lege strings, wikkel de sommen inCOALESCEeenmaal.
Ik gebruiknumericin plaats vanfloat, veiliger alternatief om afrondingsfouten te minimaliseren. -
Uw poging om verschillende waarden te verkrijgen uit de samenvoeging van
usersentasksis zinloos, aangezien je meedoet aantasknog 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
datete krijgen van eentimestampje kunt gewoon casten naardate:tl.created_on::date AS changedayMaar het is veel beter om te testen met originele waarden in de
WHEREclausule ofJOINvoorwaarde (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 borderMerk op dat een letterlijke datum wordt geconverteerd naar een
timestampom00:00van 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 taskvoeg deWHERE. toe clausule:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id) -
Het belangrijkste :EEN
LEFT [OUTER] JOINbehoudt alle rijen links van de join. EenWHEREtoevoegen clausule aan de rechter tabel kan dit effect teniet doen. In plaats daarvan verplaats de filterexpressie naar deJOINclausule. 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,projectsentask_log_entriesvoordat je alles samenvoegt metusers- zonder subquery. -
Tabelaliassen maak het schrijven van complexe query's gemakkelijker.