2016-04-24 9 views
2

Ich habe eine Beziehungstabelle namens "match_players", die etwa 20000 Zeilen hat. Es gibt eine Spalte namens "hero_id", die darstellt, welcher Held von einem Spieler in einem Spiel gewählt wurde. Alle Felder sind indiziert.schmerzhaft langsam selbst JOIN

Ich brauche einen anderen Tisch bauen aller Kombinationen von zwei Helden aus, mit einem zu zählen, wie oft sie auf dem gleichen Team waren, wie oft sie waren Feinde usw.

Meine aktuelle Abfrage über nimmt 90 Sekunden auf einem 8-Core-SSD-Server. Ich stelle mir vor, dass es etwas damit zu tun hat, dass MySQL diese massive Tabelle aller Kombinationen intern erstellt, bevor die Ergebnisse gruppiert werden.

Gibt es eine alternative Methode zum Sammeln von Kombinationen von Zeilenwerten, bei denen MySQL einfach die Tabelle scannt und neue Kombinationen aufzeichnet, sobald sie gefunden werden? Jede Eingabe wird geschätzt.

Table "match_players":

match_id | team | position | player_id | hero_id | 

56427859 | 1 |  1 | 546107 |  17 | 
56427859 | 1 |  2 | 469333 |  81 | 
56427859 | 1 |  3 | 227526 |  60 | 
56427859 | 1 |  4 | 193739 |  32 | 
56427860 | 0 |  0 | 473923 |  11 | 
56427860 | 0 |  1 | 292764 |  93 | 
56427860 | 0 |  2 | 138018 |  26 | 
56427860 | 0 |  3 | 326510 |  96 | 

etc... 

Die Abfrage:

SELECT mp1.hero_id, mp2.hero_id 
FROM match_players mp1 
INNER JOIN match_players mp2 
ON mp1.hero_id < mp2.hero_id 
WHERE mp1.team = mp2.team 
GROUP BY mp1.hero_id, mp2.hero_id 

ERKLÄREN:

id | select_type | table | type | possible_keys  | key  | key_len | ref      | rows | Extra 

1 |  SIMPLE | mp1 | ALL | faction_id,hero_id |  NULL | NULL |      NULL | 34060 | Using temporary; Using filesort 
1 |  SIMPLE | mp2 | ref | faction_id,hero_id | faction_id |  1 | beta_dota_2.mp1.faction_id | 3499 | Using where 

Update:

Da ich nur Helden brauche, die in einem Match zusammen waren, habe ich meine Abfrage auf die folgenden aktualisiert und es ist viel, viel schneller. Ich denke, es ist in ein paar Zehntelsekunden abgeschlossen.

SELECT mp1.hero_id, mp2.hero_id 
FROM match_players mp1 
INNER JOIN match_players mp2 
ON mp1.hero_id < mp2.hero_id 
WHERE mp1.team = mp2.team AND mp1.match_id = mp2.match_id 
GROUP BY mp1.hero_id, mp2.hero_id 

Ich würde noch gerne wissen, aber, wenn ich eine Liste von Kombinationen aller Helden wollte, ob im selben Spiel oder nicht, was der beste Weg, um darüber zu gehen wäre? Es ist eindeutig nicht machbar mit meiner ursprünglichen Abfrage auf einer Tabelle größer als ein paar tausend Zeilen.

+0

"Schmerzhaft zeigen" bedeutet normalerweise "Ich habe vergessen, die Tabelle zu indizieren". Was sagt EXPLAIN? Eine '<' -Klausel ist normalerweise ein Problem, direkte Übereinstimmungen sind schneller.Wenn Sie wirklich gegen die Wand sind, laden Sie vielleicht alles in einer Skriptsprache in den Speicher und verarbeiten Sie es so. – tadman

+0

@tadman - Ich habe die EXPLAIN zu meinem Beitrag hinzugefügt. – DaiBu

Antwort

0

Da ich nur Helden brauchen, die zusammen in einem Spiel waren, ich meine Anfrage an die folgende aktualisierte (Beitritt auf match_id) und es ist viel, viel schneller. Ich denke, es ist in ein paar Zehntelsekunden abgeschlossen.

SELECT mp1.hero_id, mp2.hero_id 
FROM match_players mp1 
INNER JOIN match_players mp2 
ON mp1.hero_id < mp2.hero_id 
WHERE mp1.team = mp2.team AND mp1.match_id = mp2.match_id 
GROUP BY mp1.hero_id, mp2.hero_id 
5

Für diese Abfrage:

SELECT mp1.hero_id, mp2.hero_id 
FROM match_players mp1 INNER JOIN 
    match_players mp2 
    ON mp1.hero_id < mp2.hero_id AND 
     mp1.team = mp2.team 
GROUP BY mp1.hero_id, mp2.hero_id; 

Sie möchten einen zusammengesetzten Index für match_players(team, hero_id). Das ist ein Ort, um anzufangen.

Wie ich darüber nachdenke, kann das Leistungsproblem darauf zurückzuführen sein, dass viele Spieler in einem Spiel den gleichen "Helden" wählen. Wenn dies möglich ist, dann für eine Zählung möchten Sie count(distinct match_id) anstelle von count(*). Noch wichtiger ist, dass dies große Auswirkungen auf die Leistung haben kann - abhängig davon, wie groß die Teams sind.

Möglicherweise möchten Sie diese Abfrage ausführen, um eine Vorstellung davon zu bekommen, wie oft dies geschieht:

select cnt, count(*) 
from (select match_id, hero_id, count(*) as cnt 
     from match_players 
     group by match_id, hero_id 
    ) mh 
group by cnt 
order by cnt desc; 
+0

Es gibt keine doppelten Helden in einem bestimmten Spiel. Sobald einer ausgewählt ist, wird er aus dem Pool entfernt. Ich werde versuchen, Ihren zusammengesetzten Index hinzuzufügen. – DaiBu

+0

Der zusammengesetzte Index hat nicht geholfen. Deine Anfrage hat nur eine Zeile mit cnt = 1 und count (*) = 34190 wie erwartet zurückgegeben, da es keine doppelten Helden gibt. Danke, Mann. – DaiBu

+0

@DaiBu. . . Wie viele Teams hast du? Wie groß ist das durchschnittliche Team? Vielleicht erzeugst du einfach zu viele Kombinationen. –

Verwandte Themen