2012-03-14 3 views
0

Je crée un tableau d'affichage et j'essaie de récupérer des rubriques régulières (c'est-à-dire des rubriques non collées) et de les trier par date du dernier message envoyé. Je suis capable d'accomplir ceci cependant quand j'ai environ 10.000 messages et 1500 sujets le temps de recherche est> 60 secondes.Optimisation de requête SQL lors de l'utilisation de plusieurs jointures et de plusieurs jeux d'enregistrements

Ma question est la suivante: y a-t-il quelque chose que je peux faire à ma requête pour augmenter les performances ou mon design est-il fondamentalement défectueux?

Voici la requête que j'utilise.

SELECT Messages.topic_id, 
     Messages.posted, 
     Topics.title, 
     Topics.user_id, 
     Users.username 
FROM Messages 
LEFT JOIN 
    Topics USING(topic_id) 
LEFT JOIN 
    Users on Users.user_id = Topics.user_id 
WHERE Messages.message_id IN (
    SELECT MAX(message_id) 
    FROM Messages 
    GROUP BY topic_id) 
AND Messages.topic_id 
NOT IN (
    SELECT topic_id 
    FROM StickiedTopics) 
AND Messages.posted IN (       
    SELECT MIN(posted) 
    FROM Messages 
    GROUP BY message_id) 
AND Topics.board_id=1 
ORDER BY Messages.posted DESC LIMIT 50 

Modifier Voici le plan

Expliquer
+----+--------------------+----------------+----------------+------------------+----------+---------+-------------------------+------+----------------------------------------------+ 
| id | select_type  | table   | type   | possible_keys | key  | key_len | ref      | rows | Extra          | 
+----+--------------------+----------------+----------------+------------------+----------+---------+-------------------------+------+----------------------------------------------+ 
| 1 | PRIMARY   | Topics   | ref   | PRIMARY,board_id | board_id | 4  | const     | 641 | Using where; Using temporary; Using filesort | 
| 1 | PRIMARY   | Users   | eq_ref   | PRIMARY   | PRIMARY | 4  | spergs3.Topics.user_id | 1 |            | 
| 1 | PRIMARY   | Messages  | ref   | topic_id   | topic_id | 4  | spergs3.Topics.topic_id | 3 | Using where         | 
| 4 | DEPENDENT SUBQUERY | Messages  | index   | NULL    | PRIMARY | 8  | NULL     | 1 |            | 
| 3 | DEPENDENT SUBQUERY | StickiedTopics | index_subquery | topic_id   | topic_id | 4  | func     | 1 | Using index         | 
| 2 | DEPENDENT SUBQUERY | Messages  | index   | NULL    | topic_id | 4  | NULL     | 3 | Using index         | 
+----+--------------------+----------------+----------------+------------------+----------+---------+-------------------------+------+----------------------------------------------+ 

Index

+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Messages |   0 | PRIMARY |   1 | message_id | A   |  9956 |  NULL | NULL |  | BTREE  |   | 
| Messages |   0 | PRIMARY |   2 | revision_no | A   |  9956 |  NULL | NULL |  | BTREE  |   | 
| Messages |   1 | user_id |   1 | user_id  | A   |   432 |  NULL | NULL |  | BTREE  |   | 
| Messages |   1 | topic_id |   1 | topic_id | A   |  3318 |  NULL | NULL |  | BTREE  |   | 
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Topics |   0 | PRIMARY |   1 | topic_id | A   |  1205 |  NULL | NULL |  | BTREE  |   | 
| Topics |   1 | user_id |   1 | user_id  | A   |   133 |  NULL | NULL |  | BTREE  |   | 
| Topics |   1 | board_id |   1 | board_id | A   |   1 |  NULL | NULL |  | BTREE  |   | 
+--------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Users |   0 | PRIMARY   |   1 | user_id  | A   |  2051 |  NULL | NULL |  | BTREE  |   | 
| Users |   0 | username_UNIQUE |   1 | username | A   |  2051 |  NULL | NULL |  | BTREE  |   | 
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
+0

Vous devez au minimum afficher le plan d'explication. – EvilTeach

+0

Quel RDBMS utilisez-vous? – squillman

+0

Pas de plan de requête = pas d'optimisation (bien qu'il soit possible de deviner les indices qui devraient exister, ce qui * doit aussi être indiqué dans une question). Avez-vous envisagé de remplacer 'IN' par les' JOIN's appropriés? (Je n'utilise pas MySQL, donc je ne sais pas ce qu'il fait: - /) –

Répondre

2

Je voudrais commencer avec la première base de sujets qualifiés, obtenir ces ID, puis se joindre après. Ma première requête interne effectue une pré-qualification groupée par topic_id et un message max pour obtenir des ID distincts pré-qualifiés. J'ai également appliqué un LEFT JOIN aux stickiesTopics aussi. Pourquoi? En faisant une jointure à gauche, je peux chercher ceux qui sont FOUND (ceux que vous voulez exclure). J'ai donc appliqué une clause WHERE pour l'ID de sujet Stickies est NULL (ie: NON trouvé). Donc, en faisant cela, nous avons DEJA apparié la liste SIGNIFICANTLY sans faire plusieurs sous-requêtes imbriquées. A partir de ce résultat, nous pouvons joindre aux messages, sujets (y compris le qualificateur de board_id = 1), les utilisateurs et obtenir des pièces au besoin. Enfin, appliquez une seule sous-sélection WHERE IN pour votre qualificatif MIN (posté). Ne pas comprendre la base de cela, mais laissé dans le cadre de votre requête originale. Puis la commande par et limite.

SELECT STRAIGHT_JOIN 
     M.topic_id, 
     M.posted, 
     T.title, 
     T.user_id, 
     U.username 
    FROM 
     (select 
       M1.Topic_ID, 
       MAX(M1.Message_id) MaxMsgPerTopic 
      from 
       Messages M1 
       LEFT Join StickiedTopics ST 
        ON M1.Topic_ID = ST.Topic_ID 
      where 
       ST.Topic_ID IS NULL 
      group by 
       M1.Topic_ID) PreQuery 
     JOIN Messages M 
      ON PreQuery.MaxMsgPerTopic = M.Message_ID 
      JOIN Topics T 
       ON M.Topic_ID = T.Topic_ID 
       AND T.Board_ID = 1 
       LEFT JOIN Users U 
       on T.User_ID = U.user_id 
    WHERE 
     M.posted IN (SELECT MIN(posted) 
         FROM Messages 
         GROUP BY message_id) 
    ORDER BY 
     M.posted DESC 
    LIMIT 50 
+0

C'est génial, merci beaucoup!Il a réduit la requête à 20 secondes et en augmentant légèrement le matériel à 4. Bien mieux! – Drew

1

Je suppose qu'une grande partie de votre problème réside dans votre sous-requêtes. Essayez quelque chose comme ceci:

SELECT Messages.topic_id, 
     Messages.posted, 
     Topics.title, 
     Topics.user_id, 
     Users.username 
FROM Messages 
LEFT JOIN 
    Topics USING(topic_id) 
LEFT JOIN 
    StickiedTopics ON StickiedTopics.topic_id = Topics.topic_id 
        AND StickedTopics.topic_id IS NULL 
LEFT JOIN 
    Users on Users.user_id = Topics.user_id 
WHERE Messages.message_id IN (
    SELECT MAX(message_id) 
    FROM Messages m1 
    WHERE m1.topic_id = Messages.topic_id) 
AND Messages.posted IN (       
    SELECT MIN(posted)                       
    FROM Messages m2 
    GROUP BY message_id) 
AND Topics.board_id=1 
ORDER BY Messages.posted DESC LIMIT 50 

J'ai optimisé la première sous-requête en supprimant le regroupement. La deuxième sous-requête était inutile car elle peut être remplacée par un JOIN.

Je ne suis pas sûr de ce que ce troisième sous-requête est censé faire:

AND Messages.posted IN (       
    SELECT MIN(posted)                       
    FROM Messages m2 
    GROUP BY message_id) 

je pourrais être en mesure d'aider à optimiser ce si je sais ce qu'il est censé faire. Quel est exactement posted - une date, un entier, etc? Qu'est-ce que cela représente?

+0

Messages.posted est un horodatage unix. Le tableau de messages prend également en charge la modification et conserve un historique des révisions afin que cette requête obtienne la date de la révision la plus ancienne. – Drew

Questions connexes