2016-06-06 4 views
-1

Ich habe eine MySQL-Tabelle, die die Felder enthält: rangeFrom und rangeTo.Effiziente Auswahl auf einer großen Tabelle von Bereichen

Ich möchte Zeilen mit einer Bedingung wie anfordern: rangeFrom >= ? AND rangeTo <=?innerhalb einer Verbindung.

EXPLAIN SELECT * 
FROM Version 
JOIN Contract FORCE INDEX FOR JOIN (versionRangeFrom) 
    ON Version.id >= Contract.versionRangeFrom 
AND Version.id <= Contract.versionRangeTo 
WHERE Version.completedAt = '2016-06-06 10:00:01'; 

Welche mysql erklärt wie folgt aus:

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE Version ref PRIMARY,completedAt completedAt 6 const 1 NULL 
1 SIMPLE Contract ALL versionRangeFrom NULL NULL NULL 640744 Range checked for each record (index map: 0x8) 

So hat es aber 640.744 Zeilen arbeiten, die etwa 1-2 Sekunden dauert.

jedoch Einsetzen der Version-ID in den queryworks feinen

EXPLAIN SELECT * 
FROM Contract 
WHERE 5 >= Contract.versionRangeFrom AND 5 <= Contract.versionRangeTo; 

Dies erklärt sich dann wie folgt aus:

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE Contract range versionRangeFrom versionRangeFrom 4 NULL 534 Using index condition; Using where 

Also in diesem Fall mysql nur wenn 534 Zeilen geht und das dauert nur etwa 30 ms .

Also wie bereite ich mich auf eine solche Bereichsprüfung richtig vor. Es scheint, dass mysql in diesen Fällen keine Indizes verwenden kann. Ich kann mit 2 Abfragen umgehen, aber ich hätte lieber eine.

Hier mehr Schemata:

CREATE TABLE `Version` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `completedAt` datetime DEFAULT NULL, 
    `createdAt` datetime NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `completedAt` (`completedAt`) 
) 

CREATE TABLE `Contract` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `contractId` bigint(20) unsigned NOT NULL, 
    `startAt` bigint(20) NOT NULL DEFAULT '0', 
    `endAt` bigint(20) NOT NULL DEFAULT '0', 
    `tradeStartAt` bigint(20) NOT NULL DEFAULT '0', 
    `tradeEndAt` bigint(20) NOT NULL DEFAULT '0', 
    `latestAiId` bigint(20) NOT NULL DEFAULT '0', 
    `type` varchar(4) COLLATE utf8_unicode_ci NOT NULL, 
    `daPreis` int(11) NOT NULL DEFAULT '0', 
    `lastTradePreis` int(11) NOT NULL DEFAULT '0', 
    `lastTradeVol` int(11) NOT NULL DEFAULT '0', 
    `VWAID` double NOT NULL DEFAULT '0', 
    `versionRangeFrom` int(10) unsigned NOT NULL, 
    `versionRangeTo` int(10) unsigned DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `tradeStartAt` (`tradeStartAt`), 
    KEY `contractId` (`contractId`), 
    KEY `versionRangeFrom` (`versionRangeFrom`) 
) 
+0

Bitte senden Sie ein Schema und Beispielabfragen, idealerweise mit einem ERKLÄREN. "Es scheint, dass MySQL keine Indizes verwenden kann", ist nicht wirklich etwas, mit dem wir helfen können. –

+0

@NevilleK getan. Und ich dachte, dass mein Problem ein wenig anders ist, als ich dachte, und auch ich habe einen Workaround gefunden. – Nemo64

Antwort

0

ein anderer Wert als ‚5‘ brauchen würde nicht nur 534 Zeilen zu suchen.

Das Problem (x < from AND x > to) ist nicht trivial, und für die es keine einfache Antwort gibt.

Schrumpfung der Tischgröße würde ein kleines bisschen helfen. Verwenden Sie nicht BIGINT (8 Bytes), wenn ein kleinerer Datentyp ausreicht.

Vielleicht ist die einzige wirkliche Lösung eine grundlegende Überarbeitung des Schemas und des Codes. Siehe my blog.

bearbeiten

In bestimmten Situationen, diese Unterabfrage die Zeile effizient in Frage verwendet werden können, zu finden:

(SELECT Contract.id FROM ... 
     WHERE Version.id >= Contract.versionRangeFrom 
     ORDER BY versionRangeFrom 
     LIMIT 1) 
+0

Die Versionsbereich-Felder sind nur int (4 Bytes) und sie können nicht kleiner sein. Aber ich habe wahrscheinlich zu viel von meinem Schema gepostet und die andere Range dort ist durcheinander;). Ich habe versucht, eine Tabelle mit Version zu Zeilenreferenzen zu erstellen. Problem, meine Ranges sind so groß und neue Versionen sind so üblich, dass der Tisch GB innerhalb von Minuten erreicht hat (es war immer noch schnell hart). – Nemo64

+0

Ich sehe 6 'BIGINT's. Bitte besprechen Sie die "Version" und den Zweck der Suche nach einer Reihe von Versionen. Vielleicht gibt es einen Trick, der ausgeführt werden kann. –

+0

Diese Datenbank ist also eine Kopie einer API, die alle 3 Sekunden angefordert wird. Jedes Mal, wenn ich das mache, erstelle ich eine neue Version innerhalb einer Versionstabelle (im Grunde genommen nur eine Auto_increment-ID und ein Datum).Dann wird für alle neuen Datasets der Versionsbereich auf diese neue Versions-ID gesetzt. Beim Update schaue ich was sich geändert hat und aktualisiere bei Bedarf nur die VersionRangeTo. Ich brauche diese Versionen, weil ich auf historische Daten zugreifen muss. Es gibt auch lange laufende Skripte, die bei der Arbeit konsistente Daten haben müssen. – Nemo64