it-swarm.com.de

SQL-Join-Abfrage zum Anzeigen von Zeilen mit nicht vorhandenen Zeilen in einer Tabelle

Ich versuche, einige Berichte für Mitarbeiterzeitaufzeichnungen zu erstellen.

Wir haben zwei Tabellen speziell für diese Frage. Die Mitarbeiter werden in der Tabelle Members aufgeführt und geben jeden Tag Zeiteinträge der von ihnen ausgeführten Arbeit ein und werden in der Tabelle Time_Entry Gespeichert.

Beispiel-Setup mit SQL Fiddle: http://sqlfiddle.com/#!3/e3806/7

Das Endergebnis, das ich anstrebe, ist eine Tabelle, die [~ # ~] alle [~ # ~] die Members in zeigt eine Spaltenliste und zeigt dann ihre Gesamtstunden für das in den anderen Spalten abgefragte Datum an.

Das Problem scheint zu sein, dass, wenn die Tabelle Time_Entry Für ein bestimmtes Mitglied keine Zeile enthält, jetzt eine Zeile für dieses Mitglied vorhanden ist. Ich habe verschiedene Join-Typen ausprobiert (links, rechts, innen, außen, vollständig außen usw.), aber keiner scheint mir das zu geben, was ich möchte, basierend auf dem letzten Beispiel in SQL Fiddle:

/*** Desired End Result ***/

Member_ID   | COUNTTime_Entry | TIMEENTRYDATE | SUMHOURS_ACTUAL | SUMHOURS_BILL
ADavis      | 0               | 11-10-2013    | 0               | 0
BTronton    | 0               | 11-10-2013    | 0               | 0
CJones      | 0               | 11-10-2013    | 0               | 0
DSmith      | 0               | 11-10-2013    | 0               | 0
EGirsch     | 1               | 11-10-2013    | 0.92            | 1
FRowden     | 0               | 11-10-2013    | 0               | 0

Was ich derzeit bekomme, wenn ich nach einem bestimmten Datum von 11-1 frage:

Member_ID   | COUNTTime_Entry | TIMEENTRYDATE | SUMHOURS_ACTUAL | SUMHOURS_BILL
EGirsch     | 1               | 11-10-2013    | 0.92            | 1

Was richtig ist, basierend auf der One Time Entry-Zeile vom 11.10.2013 für EGirsch, aber ich muss Nullen für die anderen Mitglieder sehen, um Berichte und schließlich ein Web-Dashboard/Bericht für diese Informationen zu erhalten.

Dies ist meine erste Frage, und während ich nach Join-Abfragen usw. gesucht habe, bin ich mir ehrlich gesagt nicht sicher, wie diese Funktion heißen könnte. Ich hoffe, dass dies kein Duplikat ist und anderen helfen wird, dies zu versuchen finde eine Lösung für ähnliche Probleme.

12
farewelldave

Vielen Dank für SQLfiddle und Beispieldaten! Ich wünschte, mehr Fragen würden auf diese Weise beginnen.

Wenn Sie alle Mitglieder möchten, unabhängig davon, ob sie einen Eintrag für dieses Datum haben, möchten Sie ein LEFT OUTER JOIN. Sie waren sehr nah an dieser Version Ein kleiner Trick bei äußeren Verknüpfungen ist jedoch, dass Sie, wenn Sie der äußeren Tabelle in der Klausel WHERE einen Filter hinzufügen, eine äußere Verknüpfung in eine innere umwandeln Join, da alle Zeilen ausgeschlossen werden, die NULL auf dieser Seite sind (weil nicht bekannt ist, ob NULL mit dem Filter übereinstimmt oder nicht).

Ich habe die erste Abfrage geändert, um eine Zeile für jedes Mitglied zu erhalten:

SELECT Members.Member_ID
      ,Time_Entry.Date_Start
      ,Time_Entry.Hours_Actual
      ,Time_Entry.Hours_Bill
FROM dbo.Members
  LEFT OUTER JOIN dbo.Time_Entry
--^^^^ changed from FULL to LEFT
  ON Members.Member_ID = Time_Entry.Member_ID
  AND Time_Entry.Date_Start = '20131110';
--^^^ changed from WHERE to AND

Ich überlasse es dem Leser als Übung, es von dort zu nehmen und die anderen Spalten, Formatierungen, COALESCE usw. hinzuzufügen.

Einige andere Hinweise:

11
Aaron Bertrand

Wenn ich in der Vergangenheit mit dieser Art von Problem konfrontiert war, habe ich eine "Zahlen" - Tabelle erstellt, um die fehlenden Zeilen zu beheben.

Ich habe meine Zahlentabelle speziell für Datumsangaben erstellt:

CREATE TABLE Dates
(
    dDate DATETIME NOT NULL CONSTRAINT PK_Dates PRIMARY KEY CLUSTERED
);

INSERT INTO Dates (dDate)
SELECT TOP(73049) DATEADD(d, -1, ROW_NUMBER() OVER (ORDER BY o.object_id)) AS dDate
FROM master.sys.objects o, master.sys.objects o1, master.sys.objects o2

Dadurch wird eine Tabelle mit einer einzelnen Zeile für jedes Datum zwischen 1900-01-01 und 2099-12-31 erstellt. Ich verwende TOP(73049), um den in meinem Beispiel generierten Datumsbereich auf diese Daten zu beschränken. Wenn Sie mit einem anderen Datumsbereich arbeiten, können Sie diese Zahl anpassen.

Als Nächstes füge ich meiner Abfrage die Tabelle dDates hinzu, sodass für jedes Datum im gewünschten Bereich für jeden member_id Eine Zeile zurückgegeben wird. Das Ergebnis wird dann als solches mit der Tabelle Time_Entry Verbunden:

SELECT MD.Member_ID,
    MD.dDate,
    T.Date_Start,
    T.Hours_Actual,
    T.Hours_Bill
FROM 
    (
        SELECT M.Member_ID, D.dDate
        FROM dbo.Dates D, dbo.Members M
        WHERE D.dDate >= '20131110' AND D.dDate < '20131112'
    ) AS MD
    LEFT JOIN dbo.Time_Entry T ON MD.Member_ID = T.Member_ID AND MD.dDate = T.Date_Start
ORDER BY MD.Member_ID, MD.dDate

Auf diese Weise können Sie einen Datumsbereich für den Bericht angeben.

Sie können die Ergebnisse weiter verfeinern, indem Sie COALESCE(...) und SUM(...) wie folgt hinzufügen:

SELECT MD.Member_ID,
    MD.dDate,
    T.Date_Start,
    SUM(COALESCE(T.Hours_Actual, 0)) AS TotalHoursActual,
    SUM(COALESCE(T.Hours_Bill, 0)) AS TotalHoursBill
FROM 
    (
        SELECT M.Member_ID, D.dDate
        FROM dbo.Dates D, dbo.Members M
        WHERE D.dDate >= '20131110' AND D.dDate < '20131112'
    ) AS MD
    LEFT JOIN dbo.Time_Entry T ON MD.Member_ID = T.Member_ID AND MD.dDate = T.Date_Start
GROUP BY MD.Member_ID, MD.dDate, T.Date_Start
ORDER BY MD.Member_ID, MD.dDate

Dies führt zu der folgenden Ausgabe für Ihre Beispieldaten:

enter image description here

4
Max Vernon