2017-02-11 3 views
2

Ich habe eine Tabelle namens 'Artikel', das ungefähr so ​​aussieht ...Optimierung many-to-many-Abfrage in MySQL

id | name 
–––––––––––– 
1 | APPLES 
2 | BANANAS 
3 | ORANGES 
4 | PEARS 

... und eine Verknüpfungstabelle 'Paare' genannt Erstellen many-to-many-Beziehungen zwischen den Elementen ...

id | item1_id | item2_id 
–––––––––––––––––––––––– 
1 | 1  | 2 
2 | 1  | 4 
3 | 2  | 3 
4 | 2  | 4 
5 | 4  | 3 

ich habe die folgende Abfrage Gegenstände zu finden, die mit einem bestimmten Element gepaart ...

SELECT * FROM items i 
WHERE 
    i.id IN (SELECT item1_id FROM pairs WHERE item2_id = 4) 
OR 
    i.id IN (SELECT item2_id FROM pairs WHERE item1_id = 4) 

Rückkehr so ​​etwas wie ...

id | name 
–––––––––––– 
1 | APPLES 
3 | ORANGES 

..., die die Arbeit erledigt, aber es läuft ziemlich langsam (mit einem kleinen Testdatenmenge von etwa 100 Artikeln, 1000 Paarungen es bereits über 75 ms ist unter).

Meine Frage ist - kann dies weiter optimiert werden, um es zu beschleunigen (z. B. mit Joins anstelle von verschachtelten Abfragen)?

Danke für jede Hilfe.

Antwort

1

Der interne Abfrageoptimierer leistet hervorragende Arbeit bei der Erstellung eines Ausführungsplans, obwohl Sie sich den Plan ansehen und Engpässe erkennen können. Dinge wie die gleiche Abfrage auf eine andere Art und Weise auszudrücken, machen im Allgemeinen keinen großen Unterschied am Ende des Tages. Sogar Abfragen, die wirklich verrückt aussehen, würden Sie überrascht sein, wie gut der Optimizer mit ihnen umgeht und wie zwei scheinbar unterschiedliche Ausdrücke derselben Abfrage letztendlich zu demselben Ergebnis führen. Wenn Sie dies ändern, um Joins zu verwenden, führt dies wahrscheinlich zu demselben oder einem ähnlichen Ausführungsplan.

Also was ich zuerst tun würde, ist ein Index für Ihre Spalte item1_id und einen separaten Index für Ihre Spalte item2_id zu erstellen. Dies wird dazu beitragen, die Leistung dieser where-Klauseln zu verbessern. Wenn das Ihren Anforderungen immer noch nicht entspricht, sehen Sie sich the Optimization chapter in the MySQL docs (für welche Version von MySQL Sie auch verwenden) an, um einen vollständigen Überblick über mögliche Strategien zu erhalten. Beachten Sie, dass es Ihnen helfen wird, starke Optimierungen zu früh zu vermeiden, besonders wenn Ihre Anwendung komplex ist. Sobald Ihre Anwendung in einem weitgehend funktionierenden Zustand ist, können Sie Engpässe besser erkennen und beheben. Aber Indizes sind immer ein einfacher und lohnender erster Schritt in jeder Entwicklungsphase.

+0

Das machte einen großen Unterschied. Die Ausführungszeit ging von ~ 74ms runter auf ~ 3ms! Ich habe noch nie zuvor Indizes verwendet, und hilfreich zu wissen, dass MySQL unterschiedlich geäußerte Abfragen ähnlich optimiert. Vielen Dank! :) – teadog

+1

@steog Nur fallen Sie nicht in die Falle der Indizierung * alles *, erstellen Indizes [wo sie helfen] (http://stackoverflow.com/questions/5446124/mysql-why-not-index-every-field).PS Mit einem größeren Dataset möchten Sie vielleicht auch mit einem einzelnen mehrspaltigen Index für (item1_id, item2_id) im Vergleich zu den beiden hier beschriebenen einspaltigen Indizes experimentieren (ich würde nicht alle drei dieser Indizes gleichzeitig machen) Zeit, es sei denn, es gibt ein echtes Bedürfnis danach. –

1

Ich denke, es wird ausreichen, Indizes auf pairs(item2_id, item1_id) und pairs(item1_id, item2_id) - zwei separate Indizes zu haben.

Allerdings ist MySQL manchmal funky über die Optimierung IN mit Unterabfragen. Ich würde dies exists mit schreiben:

SELECT i.* 
FROM items i 
WHERE EXISTS (SELECT 1 
       FROM pairs p 
       WHERE p.item2_id = 4 AND p.item1_id = i.id 
      ) OR 
     EXISTS (SELECT 1 
       FROM pairs p 
       WHERE p.item1_id = 4 AND p.item2_id = i.id 
      ); 

Diese werden garantiert die Indizes verwenden.

+0

Nach dem Hinzufügen von Indizes war EXISTS tatsächlich eine Kleinigkeit langsamer als IN. Vielen Dank aber. Wird für eine andere Zeit, ohne Zweifel hilfreich sein :) – teadog

+0

@steog. . . Sie müssen tatsächlich einen großen Datensatz testen. –

+0

Aha, danke! Ich kann nicht abstimmen, weil ich ein Neuling bin, tut mir leid. – teadog