sql >> Database teknologi >  >> RDS >> Mysql

Sådan returnerer du rækker, der mangler i tabellen - Medarbejderfraværsrapport

Hvis et "fravær" er defineret som manglende fremkomst af en række i emp_tx tabel for en bestemt empcode for en bestemt dato (dato=midnat til midnat 24 timers periode), og ...

Hvis det er acceptabelt ikke at vise et "fravær" for en dato, hvor der INGEN transaktioner er i emp_tx tabel for den dato (dvs. ekskluder en dato, hvor ALLE empkoder er fraværende på den dato), så ...

Du kan få de første fire kolonner i det angivne resultatsæt med en forespørgsel som denne:(utestet)

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

Får den femte kolonne TotalNoofAbsent returneret i det samme resultatsæt er muligt, men det vil gøre den forespørgsel virkelig rodet. Denne detalje kan muligvis håndteres mere effektivt på klientsiden, når det returnerede resultatsæt behandles.

Sådan fungerer forespørgslen

Den indbyggede visning kaldet d giver os et sæt "dato"-værdier, som vi tjekker. Brug af emp_tx tabel som en kilde til disse "dato"-værdier er en bekvem måde at gøre dette på. Ikke DATE() funktionen returnerer kun "dato"-delen af ​​DATETIME-argumentet; vi bruger en GROUP BY for at få en særskilt liste over datoer (dvs. ingen duplikerede værdier). (Det, vi leder efter, med denne inline-visningsforespørgsel, er et særskilt sæt DATE-værdier mellem de to værdier, der sendes ind som argumenter. Der er andre, mere involverede, måder at generere en liste over DATE-værdier på.)

Så længe hver "dato"-værdi, som du vil betragte som et "fravær", vises et sted i tabellen (det vil sige mindst én empcode havde en transaktion på hver dato, der er af interesse), og så længe antallet af rækker i emp_tx tabellen ikke er overdreven, så vil den inline-visningsforespørgsel fungere rimeligt godt.

(BEMÆRK:Forespørgslen i den indbyggede visning kan køres separat for at bekræfte, at resultaterne er korrekte og som vi forventer.)

Det næste trin er at tage resultaterne fra inline-visningen og udføre en CROSS JOIN operation (for at generere et kartesisk produkt) for at matche HVER empcode med HVER date vendt tilbage fra inline-visningen. Resultatet af denne operation repræsenterer enhver mulig forekomst af "deltagelse".

Det sidste trin i forespørgslen er at udføre en "anti-join"-operation ved hjælp af en LEFT JOIN og en WHERE IS NULL prædikat. LEFT JOIN (ydre joinforbindelse) returnerer alle mulige tilstedeværelsesforekomster (fra venstre side), HERUNDER dem, der ikke har en matchende række (deltagelsesrekord) fra emp_tx tabel.

"Tricket" er at inkludere et prædikat (i WHERE-sætningen), der kasserer alle de rækker, hvor der blev fundet en matchende tilstedeværelsesrecord, så det, vi står tilbage med, er alle kombinationer af empcode og date (mulige tilstedeværelsesforekomster), hvor der IKKE var INGEN MATCHENDE fremmødetransaktion.

(BEMÆRK:Jeg har målrettet efterladt referencerne til s_date (DATETIME) kolonnen "bare" i prædikaterne, og brugt interval prædikater. Dette vil gøre det muligt for MySQL at gøre effektiv brug af et passende indeks, der inkluderer den kolonne.)

Hvis vi skulle pakke kolonnehenvisningerne ind i prædikaterne inde i en funktion, f.eks. DATE(p.s_date) , så vil MySQL ikke være i stand til at gøre effektiv brug af et indeks på s_date kolonne.

Som en af ​​kommentarerne (på dit spørgsmål) påpeger, skelner vi ikke mellem transaktioner, der markerer en medarbejder, enten som "kommer ind" eller "går ud". Vi leder KUN efter eksistensen af ​​en transaktion for den empcode i en given 24-timers "midnat til midnat" periode.

Der er andre tilgange til at få det samme resultatsæt, men "anti-sammenføjningsmønsteret" viser sig normalt at give den bedste ydeevne med store sæt.

For den bedste ydeevne vil du sandsynligvis have dækkende indekser:

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

... ON emp_tx (s_date, empcode)


  1. Send parametre fra en batchfil til sqlplus-script

  2. Hvordan ændrer du datatypen for en kolonne i SQL Server?

  3. PHP-siden viser rå kode

  4. Ordreliste/ while loop php problem