ぱと隊長日誌

ブログ運用もエンジニアとしての生き方も模索中

SQL の COUNT(*) と COUNT(1) の結果は同じ

SQL の COUNT(*) と COUNT(1) は同じ結果が得られます。また、COUNT() 内の定数が何であっても結果は同じです。この理屈を解説します。

まず、COUNT の仕様を確認します。

COUNT関数には2種類ある。カーディナリティと式のカウントである。
COUNT(*)は、テーブルの行数(これを関係モデルの用語で「カーディナリティ」と呼ぶ)を返す。(中略)重要な点は、この関数はNULLも含めて数えることである。なぜなら、この関数はあくまで行全体を対象にするのであって、特定の列値だけを数えているわけではないからだ。
COUNT([ALL] <value expression>)は、<value expression>集合の要素の数を返す。NULLは計算の前に除外されるのでカウントされない。
引用元:プログラマのためのSQL 第4版

「カーディナリティと式のカウント」の違いを例で確認します。

tab 表が以下の内容だとします。

id
(NULL)
0
1

SELECT COUNT(*) FROM tab; はテーブルの行数である 3 を返します。

SELECT COUNT(id) FROM tab; は id 列の NULL を除いた要素の数である 2 を返します。

では、COUNT(1) の場合はどうなるのでしょうか。少し遠回りして、以下のクエリを考えます。

SELECT id, 1 AS val FROM tab;

このクエリの結果を以下に示します。

id val
(NULL) 1
0 1
1 1

COUNT は列を指定した場合にその列の要素の数をカウントします。この結果に対して COUNT(val) をできるとすれば、val 列の要素数である 3 と分かります。そして、この結果はテーブルの行数と等しいです。COUNT(val) を COUNT(1) に置き換えれば、SELECT COUNT(1) FROM tab; となり、COUNT(*) と COUNT(1) が同じ結果を返すと分かります。

では、COUNT() 内の定数が異なるとどうなるのでしょうか。COUNT(0) を例に考えます。

まず、SELECT id, 0 AS val FROM tab; の結果を以下に示します。

id val
(NULL) 0
0 0
1 0

COUNT(val) は val 列の要素数である 3 を返します。よって、SELECT COUNT(0) FROM tab; も COUNT(*) や COUNT(1) と同じ結果を返すとわかります。

このように、COUNT(*) と COUNT(<定数>) は同じ結果(つまりテーブルの行数)を返すことが分かります。

最後にパフォーマンスについて触れておきます。COUNT(*) よりも COUNT(1) のパフォーマンスが良いと言われることがあります。これは実装依存になるため、比較は難しいです。ただ、データベースにとって性能向上は重要なテーマであり、COUNT(*) も最適化されていると考えるのが自然でしょう。多くのデータベースで COUNT(*) と COUNT(1) に差異は無いと推測しています。