Verhindern Sie das Kopieren in eine temporäre Tabelle (SQL)

7

Ich habe zwei Tische - commentsund votes:

comments(id, text, user_id, page_id)  
votes(id, value)

Die votesTabelle enthält 2.000.000 Zeilen .

Ich habe die folgende Abfrage erstellt:

SELECT SUM(votes.value),
       comments.text,
       comments.comment_id 
FROM comments, votes 
WHERE comments.comment_id = votes.comment_id AND comments.page_id = $page_id 
GROUP BY comment_id;

Die Abfrage läuft ungefähr 12 Sekunden. Ist das normal?

SHOW PROCESSLISTsagt, dass es in eine temporäre Tabelle kopiert. Dies scheint langsam zu sein. Ist es notwendig, eine temporäre Tabelle zu verwenden?

user851171
quelle
Welche Datenbank und Version (ich gehe von MySQL aus)?
Derek Downey
@ Kevin ahh, viel bessere Formatierung. Danke ... wo ist mein Repräsentant, um das zu können :(
Derek Downey

Antworten:

12

In der Welt der RDBMS sind temporäre Tabellen eine Tatsache des Lebens. Es zeigt nur in JOINs seinen hässlichen Kopf .

Selbst der schlimmste Fall eines JOIN ist der entartete JOIN, eine Abfrage einer Tabelle.

Da temporäre Tabellen immer in unsere Abfragen (in unser Leben) eingehen, ist das Beste, was Sie tun können, temporäre Tabellen zu verhungern. Machen Sie sie so kompakt wie möglich. Was meine ich ???

Hier ist Ihre Anfrage:

SELECT SUM(votes.value),
       comments.text,
       comments.comment_id 
FROM comments, votes 
WHERE comments.comment_id = votes.comment_id AND comments.page_id = $page_id 
GROUP BY comment_id;

Bei Ihrer Abfrage wird tatsächlich eine Tabelle erstellt, die sich aus einer Verknüpfung von Kommentaren und Stimmen ergibt, deren Zeilenanzahl die Anzahl der Kommentare mal 2.000.000 ist. Da temporäre Tabellen keine Indizes haben, treten immer kartesische Verknüpfungen mit den temporären Tabellen auf. Die WHERE-Klausel wird auf dem Weg angewendet, dann die GROUP BY-Summation. Vergessen Sie nicht, dass die temporäre Tabelle auch die Textspalte enthält. Das sind viele Textdaten, die in der JOIN-Phase verschoben werden müssen.

Lassen Sie uns Ihre Anfrage umgestalten

Sie können sozusagen die WHERE-Klausel am Pass beachten. Hier ist, wie:

SELECT comment_id FROM comments WHERE page_id = $page_id;

Diese Abfrage enthält nur die erforderlichen Schlüssel aus der Kommentartabelle.

Sammeln Sie als Nächstes comment_ids aus den Stimmen

SELECT comment_id,SUM(value) sumofvalues FROM votes;

Dies ist eigentlich das Schlimmste. Mit 2 Millionen Zeilen und 4 Bytes pro comment_id und 4 Bytes für die Summe ist dies im schlimmsten Fall eine 16-MB-Tabelle.

Kombinieren Sie als Nächstes die Schlüssel der Kommentare mit den übereinstimmenden Schlüsseln in den Stimmen.

SELECT BB.* FROM
(SELECT comment_id FROM comments WHERE page_id = $page_id) AA
INNER JOIN
(SELECT comment_id,SUM(value) sumofvalues FROM votes GROUP BY comment_id) BB
USING (comment_id);

Nachdem die Schlüssel aus den Kommentaren und die Summe der Werte aus den Stimmen abgerufen wurden, besteht der letzte Teil darin, die Kommentar-IDs wieder mit der ursprünglichen Kommentartabelle zu verbinden und die Textfelder abzurufen.

SELECT
    B.sumofvalues,A.text,A.comment_id
FROM
    comments A INNER JOIN
    (
        SELECT BB.*
        FROM
            (SELECT comment_id FROM comments WHERE page_id = $page_id) AA
        INNER JOIN
            (SELECT comment_id,SUM(value) sumofvalues
            FROM votes GROUP BY comment_id) BB
        USING (comment_id)
    ) B
USING (comment_id);

Bevor diese überarbeitete Abfrage so schnell wie möglich ausgeführt werden kann, müssen Sie ordnungsgemäß indizieren.

Hier sind die Indizes, die Sie benötigen:

ALTER TABLE comments ADD INDEX pageid_commentid_ndx (page_id,comment_id);
ALTER TABLE votes ADD INDEX commentid_value_ndx (comment_id,value);

Sie möchten den ersten Index, da er Zeilen nach page_id gruppiert. Sie möchten den zweiten Index, da er Zeilen nach comment_id gruppiert. Tatsächlich werden diese beiden Indizes Deckungsindizes genannt . Warum ist das wichtig ??? Dies ist wichtig, da die Unterabfragen nur die erforderlichen Daten aus dem Index abrufen und niemals die Haupttabelle berühren. Auf die Kommentartabelle wird nur einmal zugegriffen, wenn alle benötigten Schlüssel in den Unterabfragen zusammen kompiliert wurden.

Versuche es !!!

Wenn eine Syntax nicht funktioniert, kommentieren Sie bitte die Frage und lassen Sie es mich wissen !!!

RolandoMySQLDBA
quelle
6

Angenommen, dies ist MySQL, können Sie mithilfe der EXPLAINSyntax herausfinden, wie die Abfrage ausgeführt wird:

EXPLAIN SELECT SUM(votes.value),comments.text,comments.comment_id FROM comments,votes    
WHERE comments.comment_id = votes.comment_id AND comments.page_id = $page_id 
GROUP BY comment_id;

Die erste Vermutung ist, dass Sie Indizes für die Spalten benötigen, denen Sie in der Abstimmtabelle beitreten.

Derek Downey
quelle
Dies verdient aus drei (3) Gründen eine +1: 1) Die Antwort passt zu Do-it-yourself-Personen, die lernen möchten, 2) Kürze Ihrer Antwort, 3) Sie legen die Richtung fest, in der Personen indizieren sollen, wenn EXPLAIN die Abfrage enthüllt Verhalten.
RolandoMySQLDBA