Wie erstelle ich einen Index für den Datumsteil des Felds DATETIME?
mysql> SHOW COLUMNS FROM transactionlist;
+-------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+------------------+------+-----+---------+----------------+
| TransactionNumber | int(10) unsigned | NO | PRI | NULL | auto_increment |
| WagerId | int(11) | YES | MUL | 0 | |
| TranNum | int(11) | YES | MUL | 0 | |
| TranDateTime | datetime | NO | | NULL | |
| Amount | double | YES | | 0 | |
| Action | smallint(6) | YES | | 0 | |
| Uid | int(11) | YES | | 1 | |
| AuthId | int(11) | YES | | 1 | |
+-------------------+------------------+------+-----+---------+----------------+
8 rows in set (0.00 sec)
TranDateTime wird verwendet, um Datum und Uhrzeit einer Transaktion zu speichern
Meine Tabelle enthält über 1.000.000 Datensätze und die Erklärung
SELECT * FROM transactionlist where date(TranDateTime) = '2008-08-17'
es dauert lange.
BEARBEITEN:
Schauen Sie sich diesen Blog-Beitrag zum Thema " Warum DATETIME von MySQL vermieden werden kann und sollte " an.
Antworten:
Wenn ich mich richtig erinnere, wird ein ganzer Tabellenscan ausgeführt, da Sie die Spalte durch eine Funktion führen. MySQL führt die Funktion gehorsam für jede einzelne Spalte aus und umgeht dabei den Index, da der Abfrageoptimierer die Ergebnisse der Funktion nicht wirklich kennen kann.
Was ich tun würde, ist so etwas wie:
SELECT * FROM transactionlist WHERE TranDateTime BETWEEN '2008-08-17' AND '2008-08-17 23:59:59.999999';
Das sollte Ihnen alles geben, was am 17.08.2008 passiert ist.
quelle
DATETIME
; Ihre Abfrage gibt die korrekten Ergebnisse zurück und enthält keine Zeilen mitTranDateTime=2008-08-18 00:00:00
.BETWEEN
schneller als mitwhere TranDateTime >= '2008-08-17' and TranDateTime < '2008-08-18'
?Eine weitere Option ( relevant für Version 5.7.3 und höher ) besteht darin, eine generierte / virtuelle Spalte basierend auf der datetime-Spalte zu erstellen und diese dann zu indizieren.
CREATE TABLE `table` ( `my_datetime` datetime NOT NULL, `my_date` varchar(12) GENERATED ALWAYS AS (DATE(`my_datetime`)) STORED, KEY `my_idx` (`my_date`) ) ENGINE=InnoDB;
quelle
Ich möchte nicht süß klingen, aber eine einfache Möglichkeit wäre, eine neue Spalte hinzuzufügen, die nur den Datumsteil und den Index dazu enthält.
quelle
Sie können nicht nur für den Datumsteil einen Index erstellen. Gibt es einen Grund, warum du musst?
Selbst wenn Sie einen Index nur für den Datumsteil erstellen könnten, würde der Optimierer ihn wahrscheinlich immer noch nicht für die obige Abfrage verwenden.
Ich denke, das wirst du finden
SELECT * FROM transactionlist WHERE TranDateTime BETWEEN '2008-08-17' AND '2008-08-18'
Ist effizient und macht was Sie wollen.
quelle
Ich weiß nichts über die Besonderheiten von mySql, aber was schadet es, wenn nur das Datumsfeld in seiner Gesamtheit indiziert wird?
Dann suchen Sie einfach:
select * from translist where TranDateTime > '2008-08-16 23:59:59' and TranDateTime < '2008-08-18 00:00:00'
Wenn es sich bei den Indizes um B-Bäume oder um etwas anderes handelt, das vernünftig ist, sollten diese schnell gefunden werden.
quelle
>= '2008-08-16' and ... < '2008-08-18'
. Die Zeit wird angenommen00:00:00
.Valeriy Kravchuk über eine Feature-Anfrage für genau dieses Problem auf der MySQL-Site sagte, er solle diese Methode verwenden.
"In der Zwischenzeit können Sie Zeichenspalten zum Speichern von DATETIME-Werten als Zeichenfolgen verwenden, wobei nur die ersten N Zeichen indiziert werden. Mit einer sorgfältigen Verwendung von Triggern in MySQL 5 können Sie auf der Grundlage dieser Idee eine relativ robuste Lösung erstellen."
Sie könnten eine Routine schreiben, um diese Spalte ganz einfach hinzuzufügen, und dann mit Triggern diese Spalte synchron halten. Der Index für diese Zeichenfolgenspalte sollte ziemlich schnell sein.
quelle
Die einzige und gute Lösung, die ziemlich gut funktioniert, besteht darin, den Zeitstempel als Zeit und nicht als Datum / Uhrzeit zu verwenden. Es wird als INT gespeichert und gut genug indiziert. Persönlich bin ich auf ein solches Problem in der Transaktionstabelle gestoßen, die ungefähr eine Million Datensätze enthält und stark verlangsamt wurde. Schließlich habe ich darauf hingewiesen, dass dies durch ein schlecht indiziertes Feld (Datum / Uhrzeit) verursacht wird. Jetzt läuft es sehr schnell.
quelle
Ich weiß nichts über die Besonderheiten von mySQL, aber was schadet es, wenn nur das Datumsfeld in seiner Gesamtheit indiziert wird?
Wenn Sie funktionale Magie für * Bäume verwenden, sind Hashes, ... weg, da Sie die Funktion aufrufen müssen, um Werte zu erhalten. Da Sie die bevorstehenden Ergebnisse jedoch nicht kennen, müssen Sie die Tabelle vollständig scannen.
Es gibt nichts hinzuzufügen.
Vielleicht meinen Sie so etwas wie berechnete (berechnete?) Indizes ... aber bisher habe ich dies nur in Intersystems Caché gesehen. Ich glaube nicht, dass es in relationalen Datenbanken (AFAIK) einen Fall gibt.
Eine gute Lösung ist meiner Meinung nach die folgende (aktualisiertes Clintp-Beispiel):
SELECT * FROM translist WHERE TranDateTime >= '2008-08-17 00:00:00.0000' AND TranDateTime < '2008-08-18 00:00:00.0000'
Ob Sie verwenden
00:00:00.0000
oder00:00
meiner Meinung nach macht keinen Unterschied (ich habe es im Allgemeinen in diesem Format verwendet).quelle
datetime LIKE etwas% fängt den Index auch nicht ab.
Verwenden Sie dies: WHERE datetime_field> = curdate ();
Das wird den Index erfassen
und heute: 00: 00: 00 bis heute: 23: 59: 59
abdecken. Fertig.
quelle
Was sagt "erklären"? (Führen Sie EXPLAIN SELECT * FROM Transaktionsliste aus, wobei Datum (TranDateTime) = '2008-08-17')
Wenn Ihr Index aufgrund der Funktion date () nicht verwendet wird, sollte eine Bereichsabfrage schnell ausgeführt werden:
SELECT * FROM Transaktionsliste wobei TranDateTime> = '2008-08-17' UND TranDateTime <'2008-08-18'
quelle
Anstatt einen Index basierend auf einer Funktion zu erstellen (falls dies in MySQL überhaupt möglich ist), lassen Sie Ihre where-Klausel einen Bereichsvergleich durchführen. Etwas wie:
Auf diese Weise kann die Datenbank den Index für TranDateTime verwenden (es gibt einen, oder?), Um die Auswahl durchzuführen.
quelle
Wenn das Ändern der Tabelle eine Option ist oder Sie eine neue schreiben, sollten Sie Datum und Uhrzeit in separaten Spalten mit den entsprechenden Typen speichern. Sie erhalten Leistung, indem Sie einen viel kleineren Schlüsselbereich und weniger Speicherplatz haben (im Vergleich zu einer Nur-Datum-Spalte, die von einer Datums- / Uhrzeitangabe abgeleitet ist). Dies macht es auch möglich, in zusammengesetzten Schlüsseln zu verwenden, noch vor anderen Spalten.
Im Fall von OP:
+-------------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------------+------------------+------+-----+---------+----------------+ | TransactionNumber | int(10) unsigned | NO | PRI | NULL | auto_increment | | WagerId | int(11) | YES | MUL | 0 | | | TranNum | int(11) | YES | MUL | 0 | | | TranDate | date | NO | | NULL | | | TranTime | time | NO | | NULL | | | Amount | double | YES | | 0 | | | Action | smallint(6) | YES | | 0 | | | Uid | int(11) | YES | | 1 | | | AuthId | int(11) | YES | | 1 | | +-------------------+------------------+------+-----+---------+----------------+
quelle
Erstellen Sie neue Felder mit nur den Daten
convert(datetime, left(date_field,10))
und indizieren Sie diese.quelle
date(date_field)
?