Forespørgslen kan sandsynligvis forenkles til:
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;
Dette returnerer alle brugere, der nogensinde har haft en opgave.
Plus data pr. projekt og dag hvor data findes i det angivne datointerval i task_log_entries
.
Vigtige punkter
-
aggregerfunktionen
sum()
ignorererNULL
værdier.COALESCE()
pr. række er ikke påkrævet mere, så snart du omformulerer beregningen som forskellen på to summer:,coalesce(sum(nullif(new_value, '')::numeric), 0) - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
Men hvis det er muligt, at alle kolonner i et udvalg har
NULL
eller tomme strenge, pak summene ind iCOALESCE
én gang.
Jeg brugernumeric
i stedet forfloat
, sikrere alternativ til at minimere afrundingsfejl. -
Dit forsøg på at få forskellige værdier fra sammenføjningen af
users
ogtasks
er nyttesløst, da du deltager itask
endnu en gang længere nede. Gør hele forespørgslen flad for at gøre den enklere og hurtigere. -
Disse positionsreferencer er blot en notationsbekvemmelighed:
GROUP BY 1, 2, 3 ORDER BY 1, 2, 3
... gør det samme som i din oprindelige forespørgsel.
-
For at få en
date
fra ettimestamp
du kan blot caste tildate
:tl.created_on::date AS changeday
Men det er meget bedre at teste med originale værdier i
WHERE
klausul ellerJOIN
tilstand (hvis muligt, og det er muligt her), så Postgres kan bruge almindelige indekser på kolonnen (hvis tilgængelig):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
Bemærk, at en dato bogstavelig konverteres til et
timestamp
kl.00:00
dagens på dit nuværende tidspunkt zone . Du skal vælge den næste dag og ekskluder det som øvre grænse. Eller angiv et mere eksplicit tidsstempel, f.eks.'2013-09-22 0:0 +2':: timestamptz
. Mere om udelukkelse af øvre kant: -
For kravet
every user who has ever been assigned to a task
tilføjWHERE
klausul:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
-
Vigtigst :En
LEFT [OUTER] JOIN
bevarer alle rækker til venstre for sammenføjningen. Tilføjelse af enWHERE
klausul til højre tabel kan annullere denne effekt. Flyt i stedet filterudtrykket tilJOIN
klausul. Mere forklaring her: -
Parentes kan bruges til at fremtvinge den rækkefølge, som tabeller samles i. Sjældent nødvendigt til simple forespørgsler, men meget nyttigt i dette tilfælde. Jeg bruger funktionen til at deltage i
task
,fixins
,projects
ogtask_log_entries
før du forlader det hele tilusers
- uden underforespørgsel. -
Tabelaliasser gør det nemmere at skrive komplekse forespørgsler.