2017-05-23 13 views
1
Table ideas: id, author_id, some_columns 

Table ideas_tags: idea_id, tag_name 

Table ideas_seen: idea_id, user_id 

Table user: uid, ban, some_columns 

リストからタグがあり、作成者が禁止されておらず、現在のユーザーのideas_seenにないアイデアを取得する必要があります。遅いSQLクエリを高速化するには

SELECT 
ideas.*, GROUP_CONCAT(DISTINCT IT_V.tag_name SEPARATOR '|||') AS tags 
FROM `ideas` 
LEFT JOIN ideas_tags IT 
ON ideas.id=IT.idea_id 
LEFT JOIN ideas_tags IT_V 
ON ideas.id=IT_V.idea_id 
LEFT JOIN ideas_seen IV 
ON ideas.id=IV.idea_id AND IV.user_id=145974517 
LEFT JOIN users ON users.uid=ideas.author_id 

WHERE author_id!=145974517 AND IV.id IS NULL AND ((IT.tag_name = 'some_tag') OR (IT.tag_name = 'another_tag') OR (IT.tag_name IS NULL)) AND active=1 AND deleted=0 AND (users.ban=0 OR users.ban IS NULL) 

GROUP BY ideas.id 
ORDER BY id DESC 
LIMIT 10 

それは、ウェブサイト上で最も遅いクエリであると私はそれをスピードアップする方法がわからない:

今私のクエリは次のようになります。

EXPLAIN: Explain query

CREATE TABLE IF NOT EXISTS `ideas` (
`id` int(11) NOT NULL, 
    `tutorial` tinyint(4) NOT NULL, 
    `text` text NOT NULL, 
    `author_id` int(11) NOT NULL, 
    `active` bit(1) NOT NULL, 
    `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    `views` int(11) NOT NULL, 
    `views_all` int(11) DEFAULT '0', 
    `deleted` tinyint(4) NOT NULL DEFAULT '0', 
    `many_users` tinyint(4) DEFAULT NULL, 
    `game_id` int(11) DEFAULT NULL 
) ENGINE=InnoDB AUTO_INCREMENT=35983 DEFAULT CHARSET=utf8; 

CREATE TABLE IF NOT EXISTS `ideas_seen` (
`id` int(11) NOT NULL, 
    `idea_id` int(11) NOT NULL, 
    `user_id` int(11) NOT NULL 
) ENGINE=InnoDB AUTO_INCREMENT=3694368 DEFAULT CHARSET=utf8; 

CREATE TABLE IF NOT EXISTS `ideas_tags` (
`id` int(11) NOT NULL, 
    `idea_id` int(11) NOT NULL, 
    `tag_name` tinytext NOT NULL 
) ENGINE=InnoDB AUTO_INCREMENT=86832 DEFAULT CHARSET=utf8; 

CREATE TABLE IF NOT EXISTS `users` (
    `uid` int(11) NOT NULL, 
    `email` tinytext, 
    `password_hash` tinytext, 
    `restore_code` tinytext NOT NULL, 
    `last_action` timestamp NULL DEFAULT NULL, 
    `score` float NOT NULL, 
    `date_register` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `posts_length` int(11) DEFAULT NULL, 
    `settings` text, 
    `titles` int(11) NOT NULL DEFAULT '1', 
    `filter` int(11) NOT NULL DEFAULT '1', 
    `note` text NOT NULL, 
    `ban` tinyint(4) DEFAULT NULL, 
    `mod_send` smallint(6) DEFAULT '0', 
    `mod_get` int(11) DEFAULT '0', 
    `fp_notified` int(11) NOT NULL DEFAULT '0', 
    `skilled` tinyint(4) NOT NULL DEFAULT '0', 
    `show_only_skilled` tinyint(4) NOT NULL DEFAULT '0' 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 


ALTER TABLE `ideas` 
ADD PRIMARY KEY (`id`), ADD KEY `author_id` (`author_id`), ADD KEY `game_id` (`game_id`), ADD KEY `active` (`active`), ADD KEY `many_users` (`many_users`), ADD KEY `deleted` (`deleted`), ADD KEY `tutorial` (`tutorial`), ADD FULLTEXT KEY `idea_text` (`text`); 

ALTER TABLE `ideas_seen` 
ADD PRIMARY KEY (`id`), ADD KEY `user_id` (`user_id`), ADD KEY `idea_id` (`idea_id`); 

ALTER TABLE `ideas_tags` 
ADD PRIMARY KEY (`id`), ADD KEY `tag_name` (`tag_name`(255)), ADD KEY `idea_id` (`idea_id`); 

ALTER TABLE `users` 
ADD PRIMARY KEY (`uid`), ADD UNIQUE KEY `email` (`email`(255)), ADD KEY `ban` (`ban`), ADD KEY `fp_notified` (`fp_notified`), ADD KEY `skilled` (`skilled`); 


ALTER TABLE `ideas` 
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=35983; 
ALTER TABLE `ideas_seen` 
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3694368; 
ALTER TABLE `ideas_tags` 
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=86832; 

ALTER TABLE `ideas` 
ADD CONSTRAINT `ideas_ibfk_1` FOREIGN KEY (`game_id`) REFERENCES `games` (`id`) ON DELETE NO ACTION; 

ALTER TABLE `ideas_seen` 
ADD CONSTRAINT `ideas_seen_ibfk_1` FOREIGN KEY (`idea_id`) REFERENCES `ideas` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION; 

ALTER TABLE `ideas_tags` 
ADD CONSTRAINT `ideas_tags_ibfk_2` FOREIGN KEY (`idea_id`) REFERENCES `ideas` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION; 
+0

インデックスが何であり、数字が同じであることを私たちに示していません – e4c5

+0

クエリで使用されるすべての列のインデックス。 – Alexey

+0

数字は何を意味しますか? – Alexey

答えて

1
  • TINYTEXTを使用しないでください。 VARCHAR(255)に変更し、インデックスの「接頭辞」を取り除いてください。つまり、INDEX email(255)からINDEX(email)に変更します。

  • 'flags'にはインデックスを設定しないでください。このようなインデックスは有用ではないため、使用されません。例:deleted

  • 別のインデックスの「左」部分を1つのインデックスにしないでください。例PRIMARY KEY(id)INDEX(id, ...)PRIMARY KEYの場合はそのまま使用してください。特別な利益を提供しないように他を投げる。

  • 私はidea_tagsに参加する必要はありません。あなたがそれを避けることができるかどうか見てください。

  • 「膨脹 - 収縮」​​症候群が問われます。最初にJOINsを使用して行数を増やしてから、GROUP BYを使用して元の行に戻します(除外された行数は少なくなります)。これにより、大量のideas.*が一時テーブル内を移動します。

  • TEXTTINYTEXTを含む)は、MEMORYをtmpテーブルでより効率的に使用できないようにします。

膨脹収縮の除去を歩いてみましょう。

まずは、外側部分を構造化してみましょう:私たちは???に記入することができますと仮定すると

SELECT ideas.*, (???) as tags 
    FROM ideas 
    WHERE ??? 
    ORDER BY ideas.id DESC 
    LIMIT 10; 

、我々は今、はるかに高速な評価へのパスを持っています。これには、PRIMARY KEY(id)があるidのインデックスが必要です。運があれば(そしてWHEREがなければ)、の10行だけが触れられる必要があります。 (お使いのバージョンでは、テーブル全体が、集まったグループ化、並べ替え、およびだけにして10に送達されなければならない。)

すべてのJOINsLEFT JOINsなので、我々は証明可能ideasはないでしょう以外のテーブルに触れWHERE句と言うことができます任意の行を除外します。それはそのために

WHERE author_id!=145974517 
    AND active=1 
    AND deleted=0 

を残し、(私はわからないけれども、それが使用されます)のは、持ってみましょう:

INDEX(active, deleted, author_id) 

戻るAS tagsへ...今だけtag_name値を取得するクエリをダウンストリップ私は2がにそこに参加した理由で迷子にどこ

SELECT GROUP_CONCAT(DISTINCT IT_V.tag_name SEPARATOR '|||') AS tags 
    FROM  ideas_tags IT_V ON ideas.id = IT_V.idea_id 
     AND (  IT.tag_name = 'some_tag' 
       OR IT.tag_name = 'another_tag' 
       OR IT.tag_name IS NULL 
      ) 

(そしてここにある:与えられたideas.idためGROUP_CONCATがことを確認します0)。一方、SELECTtagsを取得するサブクエリになることをお勧めします。それはLEFTあるので、ノーフィルタリングしないように思われること

うーん...何

LEFT JOIN users ON users.uid = ideas.author_id 
WHERE (users.ban=0 OR users.ban IS NULL) 

について。 usersは他のところで言及されていないので、の列を提供しているようです。だから、無駄なコードだと思っていますか?だから、

LEFT JOIN ideas_seen IV ON ideas.id = IV.idea_id 
       AND IV.user_id=145974517 
WHERE IV.id IS NULL 

ため

同上が、それはおそらく

SELECT ideas.*, 
     (SELECT GROUP_CONCAT(DISTINCT IT_V.tag_name SEPARATOR '|||') 
      FROM ideas_tags IT_V 
      WHERE ideas.id = IT_V.idea_id 
       AND ( IT.tag_name = 'some_tag' 
        OR IT.tag_name = 'another_tag' 
        OR IT.tag_name IS NULL) 
     ) as tags 
    FROM ideas 
    WHERE author_id!=145974517 
     AND active=1 
     AND deleted=0 
    ORDER BY ideas.id DESC 
    LIMIT 10; 

としてクエリを、いくつかのインデックスを削除する一つの指標を追加し、書き換えに煮詰めた、DISTINCTは不要です。

関連する問題