2009-04-01 16 views
3

Ich habe eine Benutzertabelle und eine Abstimmtabelle. Die Abstimmungs-Tabelle speichert Abstimmungen gegenüber anderen Benutzern. Und zum besseren oder schlechteren, speichert eine einzelne Zeile in der Stimmen-Tabelle die Stimmen in beiden Richtungen zwischen den zwei Benutzern.Schwerwiegendes MySQL-Leistungsproblem (Joins, temporäre Tabelle, Filesort ....)

Jetzt ist das Problem, wenn ich zum Beispiel alle Leute auflisten will, über die jemand abgestimmt hat.

Ich bin kein Experte MySQL, aber von dem, was ich herausgefunden habe, dank der ODER-Bedingung in der Erklärung beitreten, muss es durch die ganzen Benutzer-Tabelle suchen (derzeit 44.000 Zeilen), und es erstellt dazu eine temporäre Tabelle.

Momentan dauert die folgende Abfrage etwa zwei Minuten, ja, zwei Minuten abzuschließen. Wenn ich die ODER-Bedingung und alles danach in der Join-Anweisung entferne, läuft sie in weniger als einer halben Sekunde, da sie nur ungefähr 17 der 44.000 Benutzerzeilen durchsehen muss (erkläre ftw!).

Das Balg Beispiel die Benutzer-ID , und ich versuche, seine/ihre eigenen keine Stimmen zu holen, und die Informationen von Benutzern anschließen, die auf das Ergebnis gewählt.

Gibt es eine bessere und schnellere Möglichkeit, diese Abfrage auszuführen? Oder sollte ich die Tabellen neu strukturieren? Ich hoffe ernsthaft, dass dies behoben werden kann, indem ich die Abfrage modifiziere, denn es gibt bereits viele Benutzer (+44.000) und Stimmen (+130.000) in den Tabellen, die ich migrieren müsste.

Dank :)

SELECT *, votes.id as vote_id 
FROM `votes` 
LEFT JOIN users ON (
    (
    votes.user_id_1 = 9834 
    AND 
    users.uid = votes.user_id_2 
) 
    OR 
    (
    votes.user_id_2 = 9834 
    AND 
    users.uid = votes.user_id_1 
) 
) 
WHERE (
    (
    votes.user_id_1 = 9834 
    AND 
    votes.vote_1 = 0 
) 
    OR 
    (
    votes.user_id_2 = 9834 
    AND 
    votes.vote_2 = 0 
) 
) 
ORDER BY votes.updated_at DESC 
LIMIT 0, 10 

Antwort

6

Anstelle des OR, könnten Sie eine Vereinigung von 2 Abfragen tun. Ich habe Fälle kennengelernt, in denen dies in mindestens einem anderen DBMS um eine Größenordnung schneller ist, und ich nehme an, dass MySQLs Abfrageoptimierer dasselbe "Feature" verwenden kann.

SELECT whatever 
FROM votes v 
     INNER JOIN 
       users u 
       ON v.user_id_1 = u.uid 
WHERE v.user_id_2 = 9834 
AND  v.votes_2 = 0 

UNION 

SELECT whatever 
FROM votes v 
     INNER JOIN 
       users u 
       ON v.user_id_2 = u.uid 
WHERE v.user_id_1 = 9834 
AND  v.votes_1 = 0 

ORDER BY updated_at DESC 
+0

Könnten Sie bitte ein Beispiel für eine solche Abfrage posten? Ich bin weit entfernt von einem SQL-Guru, also ist es sehr nützlich, etwas echten Code zu sehen :). Auch wäre es immer noch möglich, die Ergebnisse nach der Spalte update_at zu sortieren, und für die Paginierung Limit/Offset? – jimeh

+2

Noch besser, machen Sie eine UNION ALL, wenn Duplikate nicht möglich sind (wie es in diesem Fall zu sein scheint). Eine reguläre UNION erzwingt eine Sortierung und Duplikateliminierung, die in diesem Fall nicht erforderlich sein sollte. –

+0

Entschuldigung, mein Browser muss ausgeflippt sein, da Ihre Abfrage nicht vorher angezeigt wurde ... lol Ich habe es einfach versucht, ich scheine keine Ergebnisse zu bekommen, aber das größte Problem ist, dass ich nicht scheinen kann Holen Sie die ORDER BY zu arbeiten, es klagt nur die update_at Spalte existiert nicht. Ich habe auch v.updated_at ausprobiert. – jimeh

0

Sie haben Ihre eigene Frage beantwortet: Ja, Sie sollten die Tabelle neu gestalten, da sie nicht für Sie arbeitet. Es ist zu langsam und erfordert zu komplizierte Abfragen. Glücklicherweise ist die Migration der Daten nur eine Frage der grundlegenden Frage, die Sie hier stellen, aber für alle Benutzer statt nur für eine. (Das heißt, eine Summe oder Zählung über die Gewerkschaften die erste Antwort vorgeschlagen.)