2012-03-14 11 views
0

メッセージボードを作成していて、定期的なトピック(つまり貼り付けられていないトピック)を取り出し、最後に投稿されたメッセージの日付順に並べ替えようとしています。私はこれを達成することができますが、約10,000のメッセージと1500のトピックがある場合、クエリ時間は> 60秒です。複数の結合と大レコードセットを使用する場合のSQLクエリの最適化

私の質問にはパフォーマンスを向上させるために何かできますか、私のデザインには根本的に欠陥がありますか?

私が使用しているクエリは次のとおりです。

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 

編集ここで実行計画は、私はあなたの問題の大部分は、あなたのサブクエリにあると推測

+----+--------------------+----------------+----------------+------------------+----------+---------+-------------------------+------+----------------------------------------------+ 
| 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         | 
+----+--------------------+----------------+----------------+------------------+----------+---------+-------------------------+------+----------------------------------------------+ 

インデックス

+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| 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

少なくとも、実行計画を掲載する必要があります。 – EvilTeach

+0

あなたはどのRDBMSを使用していますか? – squillman

+0

クエリプランなし=最適化なし(存在するはずのインデックスを推測することは可能かもしれませんが、質問に表示する必要もあります)。 'IN'を適切な' JOIN'で置き換えることを考えましたか? (私はMySQLを使用していないので、何をしているのかわかりません: - /) –

答えて

2

資格を持つトピックの最初の基礎から始め、それらのIDを取得してから参加します。 私の内側の最初のクエリは、pre-qualifyをtopic_idとmaxメッセージでグループ化して、事前に限定された別個のIDを取得するだけです。私はstickiesTopicsにLEFT JOINも適用しました。どうして?左結合を実行すると、除外したいもの(除外したいもの)を探すことができます。だから、StickiesトピックIDがNULL(つまり:見つからない)のWHERE句を適用しました。このようにすることで、いくつかのネストされたサブクエリを実行せずにリストを大幅にペア設定しました。その結果から、メッセージ、トピック(board_id = 1の修飾子を含む)、ユーザーに参加し、必要に応じて部品を入手することができます。最後に、MIN(posted)修飾子に1つのWHERE INサブ選択を適用します。それの基礎を理解していないが、元のクエリの一部としてそれを残しました。次に、注文と制限。

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

これはすばらしいです、ありがとう!それは20秒までのクエリを取得し、ハードウェアを少し下げることで4になりました。 – Drew

1

です。

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 

私はグループ化を削除して最初のサブクエリを最適化しました。 2番目のサブクエリはJOINに置き換えることができるため不要です。

私は、この第三のサブクエリが行うことになっているものはかなりよく分からない:

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

私はそれを行うことになっているものを知っていれば、この最適化を支援することができるかもしれません。正確にはpostedとは何か - 日付、整数など?それは何を表していますか?

+0

Messages.postedはUNIXのタイムスタンプです。掲示板は編集をサポートしており、最新の履歴を保持するので、このクエリは最も古いリビジョンの日付を取得します。 – Drew

関連する問題