はじめに
MySQLを使用する場合、COUNT(*)
とCOUNT(列名)
の違いを理解することは、正確にテーブル内の行数をカウントするために必要不可欠です。
行数を取得するために両方の式を使用しますが、結果に影響する異なる動作をしていることがあります。
この記事では、COUNT(*)
とCOUNT(列名)
の違いを説明し、それぞれをいつ使うべきかの指針を提供することを目的としています。
COUNT(*)とCOUNT(列名)の説明
COUNT(*)
COUNT(*)
式は、どの列にNULL値があっても、テーブル内の全ての行をカウントします。
COUNT(列名)
COUNT(列名)
は、指定した列に含まれる非NULL値の数をカウントします。
よくある勘違い
COUNT(*)
は各行のすべての列を読み取る必要があるが、 COUNT(
では特定の列のみを読み取るため、列名
) COUNT(
の方が高速だと思っている方がいたりします。列名
)
ですが、これは正しい場合もありますが、全然逆の場合もあります。
解説
MySQLは行全体を読まずに1つの列の内容だけを読むことはできないので、SQLはデータを含む行をディスク上の約8KBのデータページに保存します。これらのページは1つ以上の行を含み、内部メモリ (RAM) に配置されます。
1つの列(または複数)の値を確認するには、ページ全体をディスクから読み取ってメモリに配置する必要があります。
テーブルの中に何行あるかを調べたいだけなのに、データページ全体を読むのは非効率的です。そこでSQLは、インデックス存在する場合、代わりにそのインデックスを使用するようにします。
列名にインデックスがある場合、COUNT(列名)
はそのインデックスを使用して行数をカウントします。
しかし、COUNT(列名)
で指定した列名にインデックスがない場合は、データページを読んでNULL値をチェックし、行数をカウントします。
一方COUNT(*)
は、インデックスが他の列で指定されていても、そのインデックスを使って行数をカウントします。
つまり、 COUNT(
は指定したカラムにインデックスがある場合のみ使用し、列名
)COUNT(*)
はすべての列をスキャンしてインデックスがある場合に使用します。
そのため、列名にインデックスがない場合、COUNT(*)
の方がCOUNT(列名)
よりも高速になる場合があります。
COUNT(列名)はどんな時に使うのか、使うべきなのか?
- カラムの値がNULLでない行の数だけに興味がある場合。
- 列固有の分析。このような状況の典型的な例は、count()を他の集計関数と併用する場合である。
例えば、orders
テーブルがあり、商品を販売しているが、商品を無料で提供しているため、いくつかの行にNULL値があるとします。
SELECT * from `orders`;
+-----------+-----------+---------------+
| ID | product | price |
+-----------+-----------+---------------+
| 1 | Apple | NULL |
| 2 | Tomato | 25 |
| 3 | Potato | 25 |
| 4 | Honey | 40 |
| 5 | Chocolate | NULL |
| 6 | Honey | 40 |
+-----------+-----------+---------------+
ここで、販売された商品の平均価格を抽出したいとします。通常であれば、こうします。
SELECT SUM(price) / COUNT(*) FROM `orders`; // 21.6667
SUM()はNULL値を0として計算するため、これでは誤りです。
正解は、次のとおりです。
SELECT SUM(price) / COUNT(price) FROM `orders`; // 32.5
まとめ
COUNT(*)とCOUNT(列名)の違いを解説しました。
違いをよく理解し、誤った利用をしないように注意して、パフォーマンスにも注意しながら利用しましょう。
コメント