2016-06-27 3 views
-1

Ich arbeite an einem Zeitmesssystem und versuche zu ermitteln, ob es für jede Person früh oder zu spät für die geplante Schicht ist. Die Tabelle "TB_Scan" hatte ursprünglich den Personencode und die Abtastzeit, bei der es sich um ein Datetime-Feld handelt. Aufgrund der Frage, die ich hatte, entschied ich mich, die Felder scYear, scMonth und scDay hinzuzufügen, weil ich dachte, es könnte helfen.Wie macht man mehrere LINKE VERBINDUNGEN mit ODER verwenden Sie vollständig einen zusammengesetzten Index?

Es ist ein System, das berechnet, wie die Benutzer ihre Fingerabdrücke beim Betreten/Verlassen des Arbeitsplatzes scannen. Ich weiß nicht, wie es auf Englisch heißt. Ich muss feststellen, ob der Benutzer spät am Morgen ist und ob der Benutzer die Arbeit vorzeitig verlässt.

Diese Tabelle tb_scan enthält Datum und Uhrzeit, zu der ein Benutzer einen Fingerabdruck scannt.

CREATE TABLE `tb_scan` (
    `scpercode` varchar(6) DEFAULT NULL, 
    `scyear` varchar(4) DEFAULT NULL, 
    `scmonth` varchar(2) DEFAULT NULL, 
    `scday` varchar(2) DEFAULT NULL, 
    `scscantime` datetime, 
    KEY `all` (`scyear`,`scmonth`,`scday`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 

Es hat 100.000 Zeilen, so etwas wie dieses

scpercode scyear scmonth scday  scdateandtime 
000001 2010  10  10  2016-01-10 08:02:00 
000001 2010  10  10  2016-01-02 17:33:00 
000001 2010  10  11  2016-01-11 07:48:00 
000001 2010  10  11  2016-01-11 17:29:00 
000002 2010  10  10  2016-01-10 17:31:00 
000002 2010  10  10  2016-01-02 17:28:00 
000002 2010  10  11  2016-01-11 05:35:00 
000002 2010  10  11  2016-01-11 05:29:00 

Und diese tb_workday Tabelle enthält jedes Datum

CREATE TABLE `tb_workday` (
    `wdpercode` varchar(6) DEFAULT NULL, 
    `wdshift` varchar(1) DEFAULT NULL, 
    `wddate` date DEFAULT NULL 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 

Es hat Zeilen mit Datum Reihenfolge wie folgt aus:

wdpercode wdshift wddate 
000001  1  2010-10-10 
000001  1  2010-10-11 
000001  1  2010-10-12 
000001  1  2010-10-13 
000002  2  2010-10-10 
000002  2  2010-10-11 
000002  2  2010-10-12 
000002  2  2010-10-13 

Es ist eine weitere tb_shift Tabelle mit Schaltzeit

CREATE TABLE `tb_shift` (
    `shiftcode` varchar(1) DEFAULT NULL, 
    `shiftbegin2` varchar(4) DEFAULT NULL, 
    `shiftbegin` varchar(4) DEFAULT NULL, 
    `shiftmid` varchar(4) DEFAULT NULL, 
    `shiftend` varchar(4) DEFAULT NULL, 
    `shiftend2` varchar(4) DEFAULT NULL 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 

shiftcode shiftbegin2 shiftbegin shiftmid shiftend shiftend2 
     1  04:00:00  08:00:00 12:00:00 17:30:00 21:30:00 
     2  12:00:00  17:30:00 21:00:00 05:30:00 09:30:00 

ich, dass an jedem Tag bestimmen will, ist der Mitarbeiter zu spät zur Arbeit kommt oder Blätter arbeiten früh, und zu welchem ​​Zeitpunkt. Hier

SELECT wdpercode,wddate,shiftbegin,shiftend,time(tlate.scscantime) wdlate,time(tearly.scscantime) wdearly 
FROM tb_workday 
LEFT JOIN tb_shift 
    ON wdshift=shiftcode 
LEFT JOIN tb_scan tlate 
    ON wdpercode=tlate.scpercode 
    AND tlate.scyear=year(wddate) 
    AND tlate.scmonth=month(wddate) 
    AND (tlate.scday=day(wddate) 
    OR tlate.scday=day(wddate)+1) 
    AND tlate.scscantime>=ADDDATE(CONCAT(wddate,' ',shiftbegin),INTERVAL IF(shiftbegin2>shiftbegin,1,0) DAY) 
    AND tlate.scscantime<=ADDDATE(CONCAT(wddate,' ',shiftmid),INTERVAL IF(shiftbegin2>shiftmid,1,0) DAY) 
LEFT JOIN tb_scan tearly 
    ON wdpercode=tearly.scpercode 
    AND tearly.scyear=year(wddate) 
    AND tearly.scmonth=month(wddate) 
    AND (tearly.scday=day(wddate) 
    OR tearly.scday=day(wddate)+1) 
    AND tearly.scscantime>ADDDATE(CONCAT(wddate,' ',shiftmid),INTERVAL IF(shiftbegin2>shiftmid,1,0) DAY) 
    AND tearly.scscantime<ADDDATE(CONCAT(wddate,' ',shiftend),INTERVAL IF(shiftbegin2>shiftend,1,0) DAY) 

ist das Beispiel eines Ausgangs:

wdpercode wddate  shiftbegin shiftend wdlate wdearly 
000001 2016-01-10 08:00:00 17:30:00 08:02:00 (null) 
000001 2016-01-11 08:00:00 17:30:00 (null) 17:29:00 
000002 2016-01-11 17:30:00 05:30:00 17:31:00 (null) 
000002 2016-01-11 17:30:00 05:30:00 (null) 05:29:00 

diese ADDDATE(CONCAT(wddate,' ',shiftbegin),INTERVAL IF(shiftbegin2>shiftbegin,1,0) DAY) ist für Mitarbeiter, die in der Nachtschicht arbeiten, so hat es 1 Tag in die Schaltzeit hinzufügen

Das Problem Wenn ich einen Index für scscantime erstelle, weigert sich MySQL, ihn für den Vergleich zu verwenden (>=, <=, >, <). finden Sie in diesem Thread Why does MySQL not use an index for a greater than comparison?

Aus diesem Grund Ich habe die scyear, scmonth und scday Felder und kombinieren sie in einem Index zusammen mit scpercode. Und ich muss sicherstellen, dass es auch für Arbeiter berechnet wird, die in Nachtschicht arbeiten, also muss ich es mit OR scday=day(wddate)+1 Bedingung hinzufügen.

Bevor ich die OR-Bedingung hinzugefügt habe, war das Ergebnis EXPLAIN 52 Zeilen. Aber als ich die OR scday=day(wddate)+1 Bedingung hinzufügte, wurde das EXPLAIN Ergebnis 364 Zeilen, das bedeutet, dass MySQL keinen SCTAG-Teil des Index verwendet hat. Gibt es eine Möglichkeit, den gesamten Index zu verwenden, so dass das Ergebnis EXPLAIN 52 Zeilen wird? Ich habe auch versucht die +1 Teil zu entfernen und das Ergebnis ist auch 52.

+0

Möchten Sie wirklich 'table2.day = table1.month' und' table2.day = table1.month + 1' vergleichen? – KaeL

+0

@KaeL oops Sorry falsches Feld –

+0

Was ist die EXPLAIN-Ausgabe von diesem: 'EXPLAIN SELECT * FROM Tabelle1 LINKE VERBINDUNG Tabelle2 ON Tabelle2.Jahr = Tabelle1.Jahr UND Tabelle2.Monat = Tabelle1.Monat UND Tabelle2.Tag = Tabelle1.TAG UNION SELECT * FROM table1 LINKS JOIN table2 ON table2.year = table1.year UND table2.month = table1.month UND table2.day = table1.day + 1' – 1000111

Antwort

1

Aus Ihrer Anfrage (Lesbarkeit formatiert)

SELECT 
     table1.*, 
     tb21.year, 
     tb21.month, 
     tb21.day, 
     tb22.year, 
     tb22.month, 
     tb22.day 
    FROM 
     table1 
     LEFT JOIN table2 tb21 
      ON table1.year = tb21.year 
      AND table1.month = tb21.month 
      AND (tb21.day = table1.day 
       OR tb21.day = table1.day+1) 
     LEFT JOIN table2 tb22 
      ON table1.year = tb22.year 
      AND table1.month = tb22.month 
      AND (tb22.day = table1.day+2 
       OR tb22.day = table1.day+3) 

Abgesehen von Ihren restriktiven Inhalt, lässt schauen Sie versuchen, die Daten aus demselben vergleichen Tag zu Tag + 1, +2 und +3.Nehmen wir an, nur für dieses Beispiel haben Sie nur 10 Tage in der Tabelle, die vom 1. Juni bis zum 10. Juni 2016 in Ihren Table1- und Table2-Tabellen dargestellt wird.

Auch dies ist eine Annahme, dass jede Tabelle alle 10 Daten in Frage nur für einfache Zwecke warum so viele Datensätze hat. Für das Datum der Tabelle 1 vom 1. Juni 2016 qualifiziert es sich mit Tabelle 2 (tb21-Version) und gibt ZWEI Datensätze zurück. Eine für den 1. Juni und eine weitere für den 2. Juni. Nun haben Sie also zwei Datensätze in Ihrem Ergebnis. Jetzt machen Sie das noch einmal links mit Tabelle 2 (TB22-Version). Diesmal suchen Sie nach 2 und 3 Tagen, von denen Sie den 3. und 4. Juni in der Tabelle haben. Sie erhalten also ein kartesisches Ergebnis. Für den Datensatz vom 1. Juni in Tabelle 1 haben Sie nun 4 Datensätze wie folgt.

T1Year T1Month T1Day T21Day T22Day 
2016 6  1  1  3 
2016 6  1  1  4 
2016 6  1  2  3 
2016 6  1  2  4 

sich nun an, Ihre Tabelle 2 hat 3 Einträge am 2. Juni und 3 Einträge am 3. Juni und Ihre Daten zu Super aufblasen würden. Aus diesem Grund müssen Sie genauer erläutern, was Sie zu tun versuchen.

Also, keinen wahren Kontext in dem, was Sie suchen, ignorieren Sie die Tatsache, dass es Ihren Index nicht perfekt nutzt. Sie haben ein OR basierend auf dem Datum über die Tagesvergleiche. Es sollte trotzdem noch für die Abfrage genutzt werden.

+0

Danke Sie für Ihre Hilfe und es tut mir leid für die Probleme, die ich verursacht habe. Ich tue mein Bestes, so viel wie es mein Englisch zulässt. –

+0

Wenn Sie also ein [Sqlfiddle] (http://sqlfiddle.com/) bereitstellen können, laden Sie realistische Daten, @DRapp kann dies für Sie gelöst bekommen. Wenn Sie Datenunsicherheiten und -annahmen klären. – Drew

Verwandte Themen