sql >> Database >  >> RDS >> Mysql

Hoe u rijen kunt retourneren die ontbreken in de tabel - Afwezigheidsrapport werknemer

Als een "afwezigheid" wordt gedefinieerd als het niet verschijnen van een rij in de emp_tx tabel voor een bepaalde empcode voor een bepaalde datum (datum=middernacht tot 24 uur per dag), en ...

Als het acceptabel is om geen "afwezigheid" te tonen voor een datum waarop er GEEN transacties zijn in de emp_tx tabel voor die datum (d.w.z. sluit een datum uit waarop ALLE empcodes op die datum afwezig zijn), dan ...

U kunt de eerste vier kolommen van de opgegeven resultatenset krijgen met een query als deze:(niet getest)

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

Die vijfde kolom krijgen TotalNoofAbsent geretourneerd in dezelfde resultatenset is mogelijk, maar het zal die query echt rommelig maken. Dit detail kan efficiënter worden afgehandeld aan de kant van de klant, bij het verwerken van de geretourneerde resultatenset.

Hoe de zoekopdracht werkt

De inline-weergave met de alias d geeft ons een reeks "datum" -waarden die we controleren. De emp_tx . gebruiken tabel als een bron van deze "datum"-waarden is een handige manier om dit te doen. Niet de DATE() functie retourneert alleen het "datum"-gedeelte van het DATETIME-argument; we gebruiken een GROUP BY om een ​​duidelijke lijst met datums te krijgen (d.w.z. geen dubbele waarden). (Wat we met deze inline view-query zoeken, is een afzonderlijke set DATE-waarden tussen de twee waarden die als argumenten worden doorgegeven. Er zijn andere, meer betrokken manieren om een ​​lijst met DATE-waarden te genereren.)

Zolang elke "datum"-waarde die u als een "afwezigheid" beschouwt, ergens in de tabel voorkomt (dat wil zeggen, ten minste één empcode op elke interessante datum één transactie had), en zolang het aantal rijen in de emp_tx tabel niet overdreven is, dan zal de inline view-query redelijk goed werken.

(OPMERKING:de zoekopdracht in de inline-weergave kan afzonderlijk worden uitgevoerd om te controleren of de resultaten correct zijn en zoals we verwachten.)

De volgende stap is om de resultaten uit de inline-weergave te halen en een CROSS JOIN uit te voeren. bewerking (om een ​​Cartesiaans product te genereren) die overeenkomt met ELKE empcode met ELKE date geretourneerd vanuit de inline-weergave. Het resultaat van deze bewerking vertegenwoordigt elk mogelijk optreden van "aanwezigheid".

De laatste stap in de query is het uitvoeren van een "anti-join"-bewerking met behulp van een LEFT JOIN en een WHERE IS NULL predikaat. De LEFT JOIN (outer join) retourneert alle mogelijke aanwezigheidsexemplaren (vanaf de linkerkant), INCLUSIEF diegene die geen overeenkomende rij hebben (aanwezigheidsrecord) uit de emp_tx tafel.

De "truc" is om een ​​predikaat op te nemen (in de WHERE-clausule) dat alle rijen negeert waar een overeenkomend aanwezigheidsrecord is gevonden, zodat we alleen nog maar combinaties van empcode overhouden. en date (mogelijke aanwezigheidsgebeurtenissen) waar er GEEN OVEREENKOMENDE aanwezigheidstransactie was.

(OPMERKING:ik heb met opzet de verwijzingen naar de kolom s_date (DATETIME) in de predikaten "kaal" gelaten en bereikpredikaten gebruikt. Hierdoor kan MySQL effectief gebruik maken van een geschikte index die die kolom bevat.)

Als we de kolomverwijzingen in de predikaten in een functie zouden wikkelen, b.v. DATE(p.s_date) , dan kan MySQL geen effectief gebruik maken van een index op de s_date kolom.

Zoals een van de opmerkingen (op uw vraag) aangeeft, maken we geen onderscheid tussen transacties die een werknemer markeren als "binnenkomen" of "uitgaan". We zijn ALLEEN op zoek naar het bestaan ​​van een transactie voor die empcode in een bepaalde periode van 24 uur 'middernacht tot middernacht'.

Er zijn andere benaderingen om dezelfde resultatenset te krijgen, maar het "anti-join"-patroon blijkt meestal de beste prestaties te geven bij grote sets.

Voor de beste prestaties wilt u waarschijnlijk indexen afdekken:

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

... ON emp_tx (s_date, empcode)


  1. Oracle invoegen als rij niet bestaat

  2. Hoe kan ik de volledige tekst van een zoekopdracht die door PDO is gemaakt, weergeven?

  3. MYSQL last_insert_id() en gelijktijdigheid

  4. Hoe een MySQL-tabel naar JSON te converteren met PHP?