質問
一般に、次のようなクエリを実行するのは良くないことです。
SELECT * FROM `group_relations`
しかし、カウントだけが必要な場合は、テーブルを変更できますが、同じ結果が得られるため、このクエリに行く必要があります。
SELECT COUNT(*) FROM `group_relations`
またはより具体的な
SELECT COUNT(`group_id`) FROM `group_relations`
後者の方が高速になる可能性があると感じていますが、他に考慮すべきことはありますか?
更新:この場合はInnoDBを使用していますが、より具体的ではないため申し訳ありません。
解決
問題の列がNOT NULLの場合、両方のクエリは同等です。 group_idにnull値が含まれる場合、
select count(*)
すべての行をカウントしますが、
select count(group_id)
group_idがnullでない行のみをカウントします。
また、MySQLなどの一部のデータベースシステムでは、count(*)を要求するときに最適化を採用しているため、このようなクエリは特定のクエリよりも少し速くなります。
個人的には、カウントするとき、nullを安全に処理するためにcount(*)を実行しています。
他のヒント
正しく覚えていれば、MYSQLではCOUNT(*)はすべての行をカウントしますが、COUNT(column_name)は特定の列にNULL以外の値を持つ行のみをカウントします。
COUNT(*)はすべての行をカウントしますが、COUNT(column_name)は指定された列にNULL値のない行のみをカウントします。
MySQLでの注意事項:
COUNT()は、行カウントがキャッシュされるため、*または非NULL列のMyISAMテーブルで非常に高速です。 InnoDBには行カウントキャッシュがないため、列がNULLであるかどうかに関係なく、COUNT(*)またはCOUNT(column_name)のパフォーマンスに違いはありません。 この投稿の違いについて詳しくは、 MySQLパフォーマンスブログ。
SELECT COUNT(1) FROM
group_relationsを試すと、列から情報を取得しようとしないため、少し速くなります。
編集:調査を行ったところ、これは一部のデータベースでのみ発生することがわかりました。 sqlserverでは1または*を使用するのと同じですが、oracleでは1を使用する方が高速です。
どうやらmysqlでも違いはありません。sqlserverのように、パーサーはクエリをselect(1)に変更するようです。何らかの方法で誤解させてすみません。
私はこのことに興味がありました。ドキュメンテーションと理論的な答えを読んでも大丈夫ですが、経験的な証拠とそれらのバランスをとりたいです。
5,607,997レコードのあるMySQLテーブル(InnoDB)があります。テーブルは自分のプライベートサンドボックスにあるため、コンテンツは静的であり、他の誰もサーバーを使用していないことがわかります。これにより、パフォーマンスに対する外部の影響がすべて効果的に除去されると思います。 auto_incrementプライマリキーフィールド(Id)を持つテーブルがあり、where句のテストに使用するnullにならないことがわかっています(Where Ed IS NOT NULL)。
テストの実行中に発生する可能性のある他の唯一の不具合はキャッシュです。クエリが初めて実行されるときは、同じインデックスを使用する後続のクエリよりも常に遅くなります。以下では、キャッシュシーディングコールと呼びます。少し混乱させるために、データに関係なく常にtrueと評価されることがわかっているwhere句を使用して実行しました(TRUE = TRUE)。
これは私の結果です:
QueryType
| w/o WHERE | where id is not null | where true=true
COUNT()
| 9 min 30.13 sec ++ | 6 min 16.68 sec ++ | 2 min 21.80 sec ++
| 6 min 13.34 sec | 1 min 36.02 sec | 2 min 0.11 sec
| 6 min 10.06 se | 1 min 33.47 sec | 1 min 50.54 sec
COUNT(Id)
| 5 min 59.87 sec | 1 min 34.47 sec | 2 min 3.96 sec
| 5 min 44.95 sec | 1 min 13.09 sec | 2 min 6.48 sec
COUNT(1)
| 6 min 49.64 sec | 2 min 0.80 sec | 2 min 11.64 sec
| 6 min 31.64 sec | 1 min 41.19 sec | 1 min 43.51 sec
++これは、キャッシュシーディングコールと見なされます。他の部分よりも遅いことが予想されます。
結果はそれを物語っていると思います。 COUNT(Id)は通常、他のものよりも外側にあります。 Where句を追加すると、trueと評価されることがわかっている句であっても、アクセス時間が大幅に短縮されます。スイートスポットはCOUNT(Id)のように見えます... WHERE Id IS NOT NULL。
他の人の結果を見たいと思います。おそらく、より小さいテーブルや、カウントしているフィールドとは異なるフィールドに対するwhere句を使用した結果です。私が考慮していない他のバリエーションがあると確信しています。
代替手段を探す
これまで見てきたように、テーブルが大きくなると、COUNT
クエリが遅くなります。最も重要なことは、解決しようとしている問題の性質を考慮することだと思います。たとえば、多くの開発者は、結果セットの合計ページ数を決定するために、大きなレコードセットのページネーションを生成するときに<=>クエリを使用します。
<=>クエリが遅くなることがわかっているので、遅いクエリを回避するだけのページネーションコントロールを表示する別の方法を検討できます。 Googleのページネーションは優れた例です。
非正規化
特定のカウントに一致するレコードの数を絶対に知る必要がある場合は、データの非正規化の古典的な手法を検討してください。ルックアップ時に行数をカウントする代わりに、レコードの挿入時にカウンターをインクリメントし、レコードの削除時にカウンターをデクリメントすることを検討してください。
これを行う場合、べき等のトランザクション操作を使用して、これらの非正規化された値の同期を維持することを検討してください。
BEGIN TRANSACTION;
INSERT INTO `group_relations` (`group_id`) VALUES (1);
UPDATE `group_relations_count` SET `count` = `count` + 1;
COMMIT;
あるいは、RDBMSがサポートしている場合は、データベーストリガーを使用できます。
アーキテクチャによっては、memcachedなどのキャッシングレイヤーを使用して非正規化された値を格納、インクリメント、デクリメントし、キャッシュキーが見つからない場合は低速のCOUNTクエリに単純にフォールスルーすることは理にかなっています。非常に揮発性のデータがある場合、これにより全体的な書き込み競合を減らすことができますが、このような場合は、ドッグパイル効果の解決。
>MySQL ISAMテーブルには、COUNT(*)の最適化が必要で、テーブル全体のスキャンはスキップされます。
COUNTのアスタリスクは、テーブルのすべてのフィールドを選択するためのアスタリスクとは関係ありません。 COUNT(*)はCOUNT(field)より遅いと言うのは純粋なゴミです
select COUNT(*)はselect COUNT(field)よりも速いと思います。 RDBMSが<!> quot; * <!> quotの指定を検出した場合。フィールドではなくCOUNTでは、カウントをインクリメントするために何も評価する必要はありません。一方、COUNTでフィールドを指定すると、RDBMSは常にフィールドがnullであるかどうかを評価して、カウントしないようにします。
ただし、フィールドがNULL可能の場合は、COUNTでフィールドを指定します。
COUNT(*)の事実と神話:
神話:<!> quot; InnoDBはcount(*)クエリをうまく処理しません<!> quot ;:
ほとんどのcount(*)クエリは、WHERE句がある場合、すべてのストレージエンジンによって同じ方法で実行されます。そうでない場合、InnoDBはテーブル全体のスキャンを実行する必要があります。
FACT :InnoDBは、where句なしでcount(*)クエリを最適化しません
主キーなどのインデックス付き列でカウントするのが最適です。
SELECT COUNT(`group_id`) FROM `group_relations`
セバスチャンがすでに言ったように、あなたが実際に達成しようとしていることに依存するべきです、すなわち、あなたの意図を明確にしてください!行を数えるだけで 場合は、COUNT(*)に進むか、単一の列を数えるとCOUNT(column)になります。
DBベンダーもチェックする価値があるかもしれません。 Informixを使用していたとき、COUNT(*)の最適化が行われました。これは、1つまたは複数の列をカウントするのに比べて、クエリプランの実行コストが1で、数値が高くなる
SELECT COUNT(1)FROM group_relationsを試行すると、列から情報を取得しようとしないため、少し高速になります。
COUNT(1)は以前はCOUNT(*)よりも高速でしたが、最新のDBMSは列について知りたくないことを知っているほど賢いので、もはや真実ではありません
このようなことについてMySQLから得たアドバイスは、一般に、このようなトリックに基づいてクエリを最適化しようとすると、長期的には呪いになる可能性があるということです。 MySQLの歴史には、オプティマイザーの動作に依存する誰かの高性能技術が次のリリースでボトルネックになるという例があります。
質問している質問に答えるクエリを作成します。すべての行のカウントが必要な場合は、COUNT(*)を使用します。 NULL以外の列のカウントが必要な場合は、COUNT(col)WHERE col IS NOT NULLを使用します。適切にインデックスを作成し、最適化をオプティマイザーに任せます。独自のクエリレベルの最適化を試みると、ビルトインオプティマイザーの効果が低下する場合があります。
とはいえ、オプティマイザーが高速化を容易にするためにクエリでできることはありますが、COUNTはその1つではないと思います。
編集:上記の回答の統計は興味深いものです。この場合、オプティマイザーで実際に動作しているものがあるかどうかはわかりません。クエリレベルの最適化全般について話しているだけです。
私はそれが一般的に悪い考えであることを知っています このようなクエリ:
SELECT * FROM `group_relations`
しかし、カウントが必要な場合は、 それができるので、私はこのクエリに行きます 変更するテーブルですが、まだ生成されます 同じ結果。
SELECT COUNT(*) FROM `group_relations`
質問が示すように、SELECT *
が不適切な理由は、テーブルを変更するにはコードの変更が必要になる可能性があるためです。 COUNT(*)
には適用されません。 SELECT COUNT('group_id')
が提供する特殊な動作が必要になることはほとんどありません。通常、レコードの数を知りたいです。それが<=>の目的なので、使用してください。