前提・実現したいこと
ORDER BYをつけると激遅になります。
これを解決する方法はありませんか?
今のテーブルは500万行ですが、今後も増え続けます。
SELECT
accounts.id,
COUNT(CASE accounts.item WHEN 1 THEN 1 END) as count1,
COUNT(CASE accounts.item WHEN 2 THEN 1 END) as count2
FROM
accounts
GROUP BY
accounts.id
LIMIT 10000;
-- Duration for 1 query: 0.063 sec. (+ 1.078 sec. network)
SELECT
accounts.id,
COUNT(CASE accounts.item WHEN 1 THEN 1 END) as count1,
COUNT(CASE accounts.item WHEN 2 THEN 1 END) as count2
FROM
accounts
GROUP BY
accounts.id
ORDER BY
count1,
count2
LIMIT 10000;
-- Duration for 1 query: 40.610 sec. (+ 0.078 sec. network)
補足情報(言語/FW/ツール等のバージョンなど)
- MariaDBのバージョン
Ver 15.1 Distrib 10.1.11-MariaDB, for Linux (x86_64) using readline 5.1 - テーブルのインデックス
accounts.id
とaccounts.item
にINDEXはついています。
それぞれ単独でのキーをつけています。 - 実行計画(EXPLAINの結果)
- 複合キーを付けた場合(2016/05/17 14:02追記)
id, itemの順に複合キーをつけた場合早くなりました。
Duration for 1 query: 6.688 sec. (+ 1.406 sec. network)
しかし、1秒台ぐらいには抑えたいと思っております。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+3
DB 設計にそれほど詳しくないので、他のよい回答がなされることを期待しておりますが、高速化するための一つの方策は正規化レベルを落とすことではないかと思います。
今回の SQL を発行する場合、全レコードをなめないと COUNT()
の値が出ませんし、それに基づいたソートもできないことになりますので、件数が増えると (主にソート処理で) さらに激重になると予想されます。
もし、データ登録時の時間が多少増えてもよいのであれば、count1
、count2
相当は別テーブルに集計し (たとえば、count
テーブルに key
カラムと value
カラムがある、とか)、SELECT
時は JOIN
するとよいのではないでしょうか。
データ登録時にかかる時間との兼ね合いになりますが、おそらく count1
, count2
相当のレコードにはインデックスを張り、場合によっては accounts
テーブルの INSERT
にトリガを設定して count
テーブルを自動更新するとかも (MariaDB にトリガがあるのかは知りませんけれども)。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
Limit10000としていますが、上位10000には明らかに届かない値がわかっているなら、それをあらかじめ省いてソート対象行を減らすなどどうでしょう?
例えばcount1の平均値以上は上位10000件には絶対に含まれない場合、次のようなイメージです。
※実際に環境を準備できないので、早くなるのかわかりません...
(また動作テストもしていません。AVGの中でサブクエリ動作しましたっけ?)
SELECT
accounts.id,
COUNT(CASE accounts.item WHEN 1 THEN 1 END) as count1,
COUNT(CASE accounts.item WHEN 2 THEN 1 END) as count2
FROM
accounts
GROUP BY
accounts.id
HAVING COUNT(CASE item WHEN 1 THEN 1 END) < AVG(
SELECT COUNT(item)
FROM accounts
WHERE item = 1
)
ORDER BY
count1,
count2
LIMIT 10000;
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
MySQLの設定ファイル(my.cnf/my.ini)にsort_buffer_sizeという設定がありますので、
メモリをガンガンつかってもよいのであれば、これを拡張しても高速化が見込めるかと。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
私はMySqlでそこまでの件数を扱ったことがないのでやったことがないのですが、Order byにもインデックスが使われるのではなかったでしょうか。
count1とcount2の複合インデックスを試してみられてはどうでしょう。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
ソート対象のレコード数が多いですね。
以下のSQLは全く同じ環境・テーブルで試したわけではないのでダメだったらスルーして下さい。
(こちらでは130万レコード程度で試しました)
SELECT
tmp_accounts.id,
tmp_accounts.count1,
tmp_accounts.count2
FROM (SELECT accounts.id,
COUNT(CASE accounts.item WHEN 1 THEN 1 END) as count1,
COUNT(CASE accounts.item WHEN 2 THEN 1 END) as count2
FROM
accounts
GROUP BY
accounts.id
LIMIT 10000) as tmp_accounts
ORDER BY
tmp_accounts.count1,
tmp_accounts.count2 ;
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
CREATE TEMPORARY TABLES
権限が必要となりますが、インデックス付きの一時テーブルを利用してみてはいかがでしょうか?
https://mariadb.com/kb/en/mariadb/create-table/
例えば、以下のような感じです。
CREATE TEMPORARY TABLE tmp (
id INT NOT NULL,
count1 INT NOT NULL,
count2 INT NOT NULL,
INDEX (count1, count2)
);
INSERT INTO tmp SELECT
accounts.id,
COUNT(CASE accounts.item WHEN 1 THEN 1 END) as count1,
COUNT(CASE accounts.item WHEN 2 THEN 1 END) as count2
FROM
accounts
GROUP BY
accounts.id;
SELECT id, count1, count2 FROM tmp ORDER BY count1, count2 LIMIT 10000;
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.35%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼
KiyoshiMotoki
2016/05/17 11:48
インデックスは、"id"と"account"それぞれのカラムに単独のインデックスがついていますか?それとも複合インデックスですか?複合インデックスの場合、カラムを指定する順番はどうなっていますか?
退会済みユーザー
2016/05/17 13:36
explain select 〜 の結果をご提示ください。
aglkjggg
2016/05/17 13:43
お二方ご意見ありがとうございます。情報を追加しました。
退会済みユーザー
2016/05/17 14:28
Explain の結果、見ることができませんよ。
aglkjggg
2016/05/17 15:00
申し訳ありません。画像に変更しました。