Mysql
 sql >> Datenbank >  >> RDS >> Mysql

So geben Sie Zeilen zurück, die in der Tabelle fehlen - Mitarbeiterabwesenheitsbericht

Wenn eine "Abwesenheit" als das Nichterscheinen einer Zeile im emp_tx definiert ist Tabelle für einen bestimmten empcode für ein bestimmtes Datum (Datum =Mitternacht bis Mitternacht 24-Stunden-Zeitraum) und ...

Wenn es akzeptabel ist, keine "Abwesenheit" für ein Datum anzuzeigen, an dem KEINE Transaktionen im emp_tx vorhanden sind Tabelle für dieses Datum (d. h. ein Datum ausschließen, an dem ALLE Empcodes an diesem Datum fehlen), dann ...

Sie können die ersten vier Spalten der angegebenen Ergebnismenge mit einer Abfrage wie dieser abrufen:(ungetestet)

SELECT m.empcode     AS `EmpCode` 
     , m.name        AS `EmpName`
     , m.dept        AS `Department`
     , d.dt          AS `AbsentDate`
  FROM ( SELECT DATE(t.s_date) AS dt
           FROM emp_tx t
          WHERE t.s_date >= '2012-12-12' 
            AND t.s_date < DATE_ADD( '2012-12-20' ,INTERVAL 1 DAY)
          GROUP BY DATE(t.s_date)
          ORDER BY DATE(t.s_date)
       ) d
 CROSS
  JOIN master m
  LEFT
  JOIN emp_tx p
    ON p.s_date >= d.dt
   AND p.s_date <  d.dt + INTERVAL 1 DAY
   AND p.empcode = m.empcode
 WHERE p.empcode IS NULL
 ORDER
    BY m.empcode
     , d.dt

Abrufen der fünften Spalte TotalNoofAbsent in der gleichen Ergebnismenge zurückgegeben wird, ist möglich, aber es wird diese Abfrage wirklich chaotisch machen. Dieses Detail kann auf der Client-Seite effizienter gehandhabt werden, wenn die zurückgegebene Ergebnismenge verarbeitet wird.

Wie die Abfrage funktioniert

Die Inline-Ansicht mit dem Alias ​​d erhält uns eine Reihe von "date"-Werten, die wir überprüfen. Mit emp_tx Tabelle als Quelle dieser "Datums"-Werte ist eine bequeme Möglichkeit, dies zu tun. Nicht das DATE() Funktion gibt nur den "date"-Teil des DATETIME-Arguments zurück; wir verwenden ein GROUP BY um eine eindeutige Datumsliste zu erhalten (d. h. keine doppelten Werte). (Was wir bei dieser Inline-Ansichtsabfrage anstreben, ist ein bestimmter Satz von DATE-Werten zwischen den beiden als Argumenten übergebenen Werten. Es gibt andere, kompliziertere Möglichkeiten, eine Liste von DATE-Werten zu generieren.)

Solange jeder "date"-Wert, den Sie als "Abwesenheit" betrachten, irgendwo in der Tabelle erscheint (d. h. mindestens ein empcode an jedem interessierenden Datum eine Transaktion hatte) und solange die Anzahl der Zeilen in emp_tx Tabelle nicht übermäßig ist, dann wird die Inline-Ansichtsabfrage einigermaßen gut funktionieren.

(HINWEIS:Die Abfrage in der Inline-Ansicht kann separat ausgeführt werden, um zu überprüfen, ob die Ergebnisse korrekt und wie erwartet sind.)

Der nächste Schritt besteht darin, die Ergebnisse aus der Inline-Ansicht zu nehmen und einen CROSS JOIN durchzuführen Operation (um ein kartesisches Produkt zu erzeugen), um JEDEM empcode zu entsprechen mit JEDEM date aus der Inline-Ansicht zurückgegeben. Das Ergebnis dieser Operation repräsentiert jedes mögliche Auftreten von "Anwesenheit".

Der letzte Schritt in der Abfrage besteht darin, eine "Anti-Join"-Operation durchzuführen, indem ein LEFT JOIN verwendet wird und ein WHERE IS NULL Prädikat. Der LEFT JOIN (Outer Join) gibt jedes mögliche Anwesenheitsvorkommen (von der linken Seite) zurück, EINSCHLIESSLICH derjenigen, die keine übereinstimmende Zeile (Anwesenheitsdatensatz) aus emp_tx haben Tabelle.

Der „Trick“ besteht darin, ein Prädikat (in die WHERE-Klausel) aufzunehmen, das alle Zeilen verwirft, in denen ein übereinstimmender Anwesenheitsdatensatz gefunden wurde, sodass wir alle Kombinationen von empcode übrig haben und date (mögliche Anwesenheitsereignisse), bei denen es KEINE ÜBEREINSTIMMENDE Anwesenheitstransaktion gab.

(HINWEIS:Ich habe die Verweise auf die Spalte s_date (DATETIME) in den Prädikaten absichtlich "leer" gelassen und Bereichsprädikate verwendet. Dadurch kann MySQL einen geeigneten Index effektiv nutzen, der diese Spalte enthält.)

Wenn wir die Spaltenreferenzen in die Prädikate innerhalb einer Funktion einschließen würden, z. DATE(p.s_date) , dann kann MySQL einen Index für s_date nicht effektiv nutzen Spalte.

Wie einer der Kommentare (zu Ihrer Frage) betont, machen wir keinen Unterschied zwischen Transaktionen, die einen Mitarbeiter entweder als "kommend" oder "gehend" kennzeichnen. Wir suchen NUR nach der Existenz einer Transaktion für diesen Empcode in einem bestimmten 24-Stunden-Zeitraum von "Mitternacht bis Mitternacht".

Es gibt andere Ansätze, um die gleiche Ergebnismenge zu erhalten, aber das "Anti-Join"-Muster liefert normalerweise bei großen Mengen die beste Leistung.

Um die beste Leistung zu erzielen, möchten Sie wahrscheinlich abdeckende Indizes:

... ON master (empcode, name, dept)

... ON emp_tx (s_date, empcode)