2012-06-20 9 views
9

ich eine Abfrage renne dieüber einen Index in MySQL JOIN mit OR-Bedingung

SELECT parent.field, child.field 
FROM parent 
JOIN child ON (child.id = parent.id 
    OR child.id = parent.otherid) 

wie folgt aussieht Dies ist jedoch sehr langsam (ca. 100k Aufzeichnungen, und schließt sich mit anderen Tabellen in der realen Version), aber trotz der versuchten Indizes auf

parent.id (PRIMARY), 
parent.otherid, 
child.id (PRIMARY), 
and a composite index of parent.id and parent.otherid 

Ich kann MySQL nicht zu einem dieser Indizes bei der Herstellung dieser beitreten zu bekommen.

Ich habe gelesen, dass MySQL nur einen Index pro Join verwenden kann, aber nirgendwo finden kann, ob es einen zusammengesetzten Index verwenden kann, wenn ein JOIN eine OR-Bedingung enthält.

Kann hier jemand wissen, ob es möglich ist, diese Abfrage auf einen Index zu verweisen? Wenn ja, wie?


meine Lösung

(SO läßt mich nicht meine eigene Frage unter atm antworten)

Ein Bündel von Zwicken und kam mit einer ziemlich anständigen Lösung auf, die die Fähigkeit und JOIN behält Aggregieren Sie andere Tabellen.

SELECT parent.field, child.field 
FROM parent 
JOIN (
    SELECT parent.id as parentid, 
    # Prevents the need to union 
    IF(NOT ISNULL(parent.otherid) AND parent.otherid <> parent.id, 
     parent.otherid, 
     parent.id) as getdataforid 
    FROM parent 
    WHERE (condition) 
) as foundrecords 
    ON foundrecords.parentid = parent.id 
JOIN child ON child.id = parent.getdataforid 

Für Geschwindigkeit erfordert eine Bedingung innerhalb der Unterabfrage die Anzahl der Datensätze in einer temporären Tabelle platziert zu reduzieren, aber ich habe Tonnen von zusätzlichen schließt sich auf der äußeren Abfrage, einige mit dem Kind verbinden und einige der Mutter (mit einigen Aggregaten), so hat dieses für mich am besten funktioniert.

In vielen Fällen wird eine Union schneller und effektiver, aber da ich auf Eltern filtern, aber zusätzliche Daten von Kind (Eltern Selbstreferenzen) wollen, verursachte die Vereinigung zusätzliche Zeilen für mich, die ich nicht konnte konsolidieren. Es ist möglich, dass das gleiche Ergebnis gefunden werden kann, indem man sich mit sich selbst verbindet und eine Where-Bedingung in der äußeren Abfrage aliasiert, aber dieses funktioniert ganz gut für mich.

Dank Jirka für die UNION ALL-Vorschlag ist es, was mich dazu veranlasst, hier zu bekommen :)

+0

Haben Sie laufen 'erklären wählen ...' auf der Abfrage? –

+1

Yep, das Erklären ist, was mich wissen lässt, dass es keine Indizes für diesen Join verwendet. –

Antwort

7

Ihre Anfrage, dass ein einzelnes Kind hat zwei verschiedene Eltern theoretisch möglich macht, die es durchaus nicht dem Standard entsprechende Terminologie machen würde. Nehmen wir jedoch an, dass Ihre Datenmuster das unmöglich machen.

Dann gibt das folgende gibt Ihnen das gleiche Ergebnis mit separaten Indizes, ein Index pro Spalte.

SELECT parent.field, child.field 
FROM parent 
JOIN child ON child.id = parent.id 

UNION ALL 

SELECT parent.field, child.field 
FROM parent 
JOIN child ON child.id = parent.otherid 
+0

+1. genau das, was ich vorschlagen würde zu versuchen, dies in zwei separate Abfragen zu brechen und die Ergebnisse zu kombinieren, obwohl diese Abfrage mehr Zeilen als die ursprüngliche Abfrage zurückgeben könnte. (Betrachten Sie eine Zeile im Elternteil, in der id = otherid). Um die gleichen Ergebnisse zu garantieren, fügen Sie das Prädikat AND parent hinzu.otherid <> parent.id' für diese zweite Abfrage. (NULLs sind kein Problem in diesem Test, da parent.id NOT NULL ist, weil es PRIMARY KEY ist.) – spencer7593

+1

Dies ist eine ausgezeichnete Lösung im vereinfachten Fall (Danke), aber es verhindert die Aggregation von Daten in Kind (mit GROUP_CONCAT (DISTINCT Kind.Feld)). Ich spiele jedoch mit der Idee, um zu sehen, ob ich es anwenden kann, um ähnliche Abfragen zu beschleunigen, aber ohne die Aggregation. Danke: D –

+2

@BobDavies - Sie können. Betten Sie dies als Unterabfrage ein. –

0
EXPLAIN 
SELECT parent.fld, child.fld 
    FROM parent JOIN child ON child.id = parent.id 
UNION ALL 
SELECT parent.fld, child.fld 
    FROM parent JOIN child ON child.id = parent.otherid 
    AND parent.otherid <> parent.id 

mit Tabellen MyISAM-Engine:

id select_type TABLE  TYPE possible_keys KEY  key_len ref     ROWS Extra 
1 PRIMARY  parent  ALL  PRIMARY            9999 
1 PRIMARY  child  eq_ref PRIMARY  PRIMARY 4  test.parent.id  1 
2 UNION   parent  ALL  parent_ix1           9999 USING WHERE 
2 UNION   child  eq_ref PRIMARY  PRIMARY 4  test.parent.otherid 1 
    UNION RESULT <union1,2> ALL 

mit Tabellen InnoDB-Engine:

id select_type table  type possible_keys key   key_len ref   rows Extra 
1 PRIMARY  child  ALL  PRIMARY           9903 
1 PRIMARY  parent  eq_ref PRIMARY  PRIMARY  4  test.child.id 1 
2 UNION   child  ALL  PRIMARY           9903 
2 UNION   parent  ref  parent_ix1  parent_ix1 5  test.child.id 1  Using where 
    UNION RESULT <union1,2> ALL