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


11

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

Wir haben zwei Tabellen speziell für diese Frage. Die Mitarbeiter werden in der MembersTabelle aufgelistet und geben jeden Tag Zeiteinträge der von ihnen geleisteten Arbeit ein und werden in der Time_EntryTabelle gespeichert .

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

Das Endergebnis werde mich für eine Tabelle , die zeigt ALL die Membersin einer Spaltenliste und dann ihre Summe Stunden für das Datum in den anderen Spalten abgefragt zeigen.

Das Problem scheint zu sein, dass, wenn Time_Entryfür ein bestimmtes Mitglied keine Zeile in der Tabelle vorhanden ist, 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 auch anderen hilft, eine Lösung zu finden ähnliche Probleme.

Antworten:


11

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

Wenn Sie möchten, dass alle Mitglieder unabhängig davon, ob sie einen Eintrag für dieses Datum haben, möchten Sie eine LEFT OUTER JOIN. Sie waren mit dieser Version sehr nah dran, aber ein kleiner Trick bei äußeren Verknüpfungen besteht darin, dass Sie, wenn Sie der äußeren Tabelle in der WHEREKlausel einen Filter hinzufügen , eine äußere Verknüpfung in eine innere Verknüpfung umwandeln, da alle Zeilen NULLauf dieser Seite ausgeschlossen werden (weil es nicht weiß, ob NULLes zum Filter passt 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 COALESCEusw. hinzuzufügen .

Einige andere Hinweise:


Aaron, vielen Dank für das Feedback. SQL-Neuling hier und hatte keine Ahnung, den Unterschied zwischen WHEREund AND. Ich hatte ursprünglich Aliase verwendet, aber sqlfiddle schien es nicht zu mögen, also ging ich einfach ins Vollformat. Vielen Dank auch für die anderen SQL-Tipps. Würden Sie empfehlen ISNULLoder COALESCEdie Daten 0 statt machen NULL? Danke noch einmal!
Abschied vom

1
@farewelldave Ich bevorzuge COALESCE, weil es Standard ist und nicht von seiner Funktionalität in anderen Sprachen abweicht (vergleiche zum Beispiel, wie ISNULL in SQL Server mit VB funktioniert). In fast allen Fällen spielt der Leistungsunterschied bis auf einen keine Rolle. Viele weitere Details hier .
Aaron Bertrand

4

Wenn ich in der Vergangenheit mit solchen Problemen 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 TOP(73049)beschränke den in meinem Beispiel generierten Datumsbereich auf diese Daten. Wenn Sie mit einem anderen Datumsbereich arbeiten, können Sie diese Zahl anpassen.

Als Nächstes füge ich die dDatesTabelle meiner Abfrage hinzu, sodass für jedes Datum eine Zeile im gewünschten Bereich für jedes zurückgegeben wird member_id. Das Ergebnis wird dann Time_Entryals solches mit der Tabelle 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 das Hinzufügen COALESCE(...)und SUM(...)gemäß:

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:

Geben Sie hier die Bildbeschreibung ein


Danke, Max. Sie können viele Informationen zu dieser Technik finden, indem Sie nach "Tally Table" anstelle von "Numbers Table" suchen. Sie eignen sich hervorragend zur Verbesserung der Leistung, indem sie Operationen mit Cursorn / Schleifen in Operationen mit Mengen konvertieren. Relationale Datenbanken bevorzugen Mengen.
Suncat2000

1
@ Suncat2000 - stimmte zu, obwohl ich den Namen "Zahlentabelle" bevorzuge, da Tally Addition impliziert, und meiner Erfahrung nach wird dieses Muster selten für mathematische Operationen verwendet. Sie eignen sich hervorragend für viele Dinge, aber eine der größten Leistungsverbesserungen, die Sie erzielen können, besteht darin, mithilfe einer Zahlentabelle von einem RBAR-Ansatz zu einem satzbasierten Ansatz überzugehen.
Max Vernon
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.