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()ignoriertNULLWerte.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 hoursAllerdings wenn es ist möglich, dass alle Spalten einer Auswahl haben
NULLoder leere Zeichenfolgen, packen Sie die Summen inCOALESCEeinmal.
Ich verwendenumericstattfloat, sicherere Alternative zur Minimierung von Rundungsfehlern. -
Ihr Versuch, eindeutige Werte aus dem Beitritt von
userszu erhalten undtasksist zwecklos, da Sietaskbeitreten 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
datezu erhalten von einemtimestampSie können einfach aufdateumwandeln :tl.created_on::date AS changedayAber es ist viel besser, mit Originalwerten in
WHEREzu testen -Klausel oderJOINBedingung (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 borderBeachten Sie, dass ein Datumsliteral wird in einen
timestampumgewandelt um00:00des 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':: timestamptzan . Mehr zum Ausschließen des oberen Randes: -
Für die Anforderung
every user who has ever been assigned to a taskfüge dasWHEREhinzu Klausel:WHERE EXISTS (SELECT 1 FROM tasks t1 WHERE t1.assignee_id = u.id) -
Am wichtigsten :Ein
LEFT [OUTER] JOINbehält alle Zeilen links vom Join bei. Hinzufügen einesWHEREKlausel auf der Rechts Tabelle kann diesen Effekt aufheben. Verschieben Sie stattdessen den Filterausdruck inJOINKlausel. 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
taskbeizutreten ,fixins,projectsundtask_log_entriesbevor Sie alles mitusersverbinden - ohne Unterabfrage. -
Tabellenaliase das Schreiben komplexer Abfragen vereinfachen.