Die Abfrage kann wahrscheinlich vereinfacht werden zu:
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;
Dies gibt alle Benutzer zurück, die jemals eine Aufgabe hatten.
Plus Daten pro Projekt und Tag wobei Daten im angegebenen Datumsbereich in task_log_entries
vorhanden sind .
Wichtige Punkte
-
Die Aggregatfunktion
sum()
ignoriertNULL
Werte.COALESCE()
pro Zeile wird nicht mehr benötigt, sobald Sie die Berechnung als Differenz zweier Summen umformen:,coalesce(sum(nullif(new_value, '')::numeric), 0) - coalesce(sum(nullif(old_value, '')::numeric), 0) AS hours
Allerdings wenn es ist möglich, dass alle Spalten einer Auswahl haben
NULL
oder leere Zeichenfolgen, packen Sie die Summen inCOALESCE
einmal.
Ich verwendenumeric
stattfloat
, sicherere Alternative zur Minimierung von Rundungsfehlern. -
Ihr Versuch, eindeutige Werte aus dem Beitritt von
users
zu erhalten undtasks
ist zwecklos, da Sietask
beitreten noch einmal weiter unten. Reduzieren Sie die gesamte Abfrage, um sie einfacher und schneller zu machen. -
Diese Positionsreferenzen sind nur eine Notationshilfe:
GROUP BY 1, 2, 3 ORDER BY 1, 2, 3
... das Gleiche tun wie in Ihrer ursprünglichen Anfrage.
-
Um ein
date
zu erhalten von einemtimestamp
Sie können einfach aufdate
umwandeln :tl.created_on::date AS changeday
Aber es ist viel besser, mit Originalwerten in
WHERE
zu testen -Klausel oderJOIN
Bedingung (falls möglich, und es ist hier möglich), sodass Postgres einfache Indizes für die Spalte verwenden kann (falls verfügbar):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
Beachten Sie, dass ein Datumsliteral wird in einen
timestamp
umgewandelt um00:00
des Tages zu Ihrer aktuellen Zeit Zone . Sie müssen Weiter auswählen Tag und ausschließen es als obere Grenze. Oder geben Sie ein expliziteres Zeitstempel-Literal wie'2013-09-22 0:0 +2':: timestamptz
an . Mehr zum Ausschließen des oberen Randes: -
Für die Anforderung
every user who has ever been assigned to a task
füge dasWHERE
hinzu Klausel:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id)
-
Am wichtigsten :Ein
LEFT [OUTER] JOIN
behält alle Zeilen links vom Join bei. Hinzufügen einesWHERE
Klausel auf der Rechts Tabelle kann diesen Effekt aufheben. Verschieben Sie stattdessen den Filterausdruck inJOIN
Klausel. Mehr Erklärung hier: -
Klammern kann verwendet werden, um die Reihenfolge zu erzwingen, in der Tabellen verknüpft werden. Wird selten für einfache Abfragen benötigt, ist in diesem Fall jedoch sehr nützlich. Ich verwende die Funktion, um
task
beizutreten ,fixins
,projects
undtask_log_entries
bevor Sie alles mitusers
verbinden - ohne Unterabfrage. -
Tabellenaliase das Schreiben komplexer Abfragen vereinfachen.