Ich habe gerade ein Protokollierungssystem eingerichtet, das aus mehreren Tabellen mit demselben Layout besteht.
Für jede Datenquelle gibt es eine Tabelle.
Für den Log Viewer möchte ich
- UNION alle Protokolltabellen ,
- filtern Sie sie nach Konto ,
- Fügen Sie eine Pseudospalte zur Identifizierung der Quelle hinzu.
- sortiere sie nach Zeit ,
- und begrenzen sie für die Paginierung .
Alle Tabellen enthalten ein Feld mit dem Namen zeitpunkt
indizierte Datums- / Uhrzeitspalte.
Mein erster Versuch war:
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
'hp' AS source FROM is_log AS l WHERE l.account_id = 730)
UNION
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730)
ORDER BY zeit DESC LIMIT 10;
Der Optimierer kann die Indizes hier nicht verwenden, da alle Zeilen aus beiden Tabellen von den Unterabfragen zurückgegeben und nach dem sortiert werden UNION
.
Meine Problemumgehung war folgende:
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
'hp' AS source FROM is_log AS l WHERE l.account_id = 730
ORDER BY l.zeitpunkt DESC LIMIT 10)
UNION
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730
ORDER BY l.zeitpunkt DESC LIMIT 10)
ORDER BY zeit DESC LIMIT 10;
Ich hatte erwartet, dass die Abfrage-Engine die Indizes hier verwenden würde, da beide Unterabfragen bereits vor dem sortiert und begrenzt werden sollten UNION
, wodurch die Zeilen zusammengeführt und sortiert werden.
Ich dachte wirklich, das wäre es, aber EXPLAIN
wenn ich die Abfrage ausführe, werden die Unterabfragen immer noch in beiden Tabellen durchsucht.
EXPLAINing
Die Unterabfragen selbst zeigen mir die gewünschte Optimierung, aber UNIONing
zusammen nicht.
Habe ich etwas verpasst?
Ich weiß, dass ORDER BY
Klauseln in UNION
Unterabfragen ohne a ignoriert werden LIMIT
, aber es gibt eine Grenze.
Bearbeiten:
Eigentlich wird es wahrscheinlich auch Abfragen ohne dieaccount_id
Bedingung geben.
Die Tabellen existieren bereits und sind mit Daten gefüllt. Abhängig von der Quelle kann es zu Änderungen im Layout kommen, daher möchte ich sie geteilt halten. Darüber hinaus verwenden die Protokollierungsclients aus einem bestimmten Grund unterschiedliche Anmeldeinformationen.
Ich muss eine Art Schicht zwischen den Protokolllesern und den tatsächlichen Tabellen halten.
Hier sind die Ausführungspläne für die gesamte Abfrage und die erste Unterabfrage sowie das Tabellenlayout im Detail:
(account_id, zeitpunkt)
. Haben Sie einen solchen Index? Das zweitbeste wäre (glaube ich) die Single(zeitpunkt)
- aber die Effizienz, wenn dies verwendet wird, hängt davon ab, wie oft Zeilen mitaccount_id=730
erscheinen.UNION DISTINCT
? Es ist nicht erforderlich, dort eine Sortierung und Unterscheidung zu erzwingen, da die Ergebnisse aufgrund der zusätzlichen Identifikationsspalte je nach Unterabfrage unterschiedlich sind. Verwenden SieUNION ALL
.source
Spalte in derselben Tabelle zu haben ? Auf diese Weise können SieUNION
s vermeiden und Indizes für alle Ihre Daten verwenden.UNION ALL
anderen Ausführungsplan führt.Antworten:
Können Sie diese Version aus Neugier ausprobieren? Es kann den Optimierer dazu verleiten, dieselben Indizes zu verwenden, die die Unterabfragen separat verwenden würden:
Ich denke immer noch, dass der beste Index, den Sie haben könnten, die Verbindung ist
(account_id, zeitpunkt)
. Es würde die 10 Reihen schnell ergeben, und es wären keine Tricks nötig.quelle
log entries / user
Wille skalieren lassen.account_id=?
, behalten Sie beide bei.SELECT * FROM
MySQL dazu verleitet , die Indizes zu verwenden?(SELECT ...) AS a
, versucht er normalerweise , die abgeleitete Tabelle getrennt von den anderen abgeleiteten Tabellen und dann der gesamten Abfrage auszuwerten und zu optimieren.force index
eine bessere Lösung.