SQLのcount関数のサンプルコードを紹介致します。
なお、当記事はMySQLのサンプルデータベースEmployeesを、SQL実行結果の表示にはphpMyAdminを使って解説しています。
countの基本的な使い方
countは、条件に合ったレコードの件数を数える関数です。
以下のように使います。
SELECT count(*) FROM employees where first_name = 'Anneke'
実行結果はこうなります。
countのサンプルコード集
countで同一テーブルの異なる条件に合致するレコードの件数を数えるサンプルコード
SELECT count(first_name = 'Anneke' or NULL), count(first_name = 'Georgi' or NULL) FROM employees
count関数では、関数内に条件を記述することが可能です。
「first_name = ‘Anneke’ or NULL」は、条件に合致したレコードは真(1)になり、そうでなければNULLになります。count関数はNULLをカウントしないため、first_name=’Anneke’という条件に合致したレコードの件数を出力することになります。
この記述方法を使えば、一つのSQLで異なる条件に合致するレコード件数を取得できます。
上記SQLは、employees(社員テーブル)から、first_name(姓名の名)が「Anneke」のレコード件数と、first_nameが「Georgi」のレコード件数を取得します。
実行すると、こうなります。
countでグループごとのレコードの件数を数えるサンプルコード
countとgroup byと組み合わせると、グループごとのレコード件数を数えられます。
SELECT title, count(*) FROM titles WHERE to_date="9999-01-01" group by title
上記SQLは、titles(肩書テーブル)から、to_date(肩書の有効期限開始日)=9999-01-01(現在有効)のデータをtitle(肩書)ごとにグルーピングし、肩書と件数を取得するSQLです。
実行すると、こうなります。
【関連記事】
▶SQLのgroup byサンプルコード集 count、like、join等の組み合わせ例
countでデータの種類数を数えるサンプルコード
countとdistinctと組み合わせて、重複しないデータをカウントすることで、データの種類数を取得することができます。
SELECT dept_name, count(distinct title) FROM `titles` left join dept_emp on titles.emp_no = dept_emp.emp_no left join departments on dept_emp.dept_no = departments.dept_no group by dept_name order by dept_name
上記SQLは、dept_name(部署名)と社員の肩書の数(データの種類)をカウントします。titles(肩書テーブル)、dept_emp(部署・社員紐付けテーブル)、departments(部署テーブル)をleft joinで結合し、dept_name(部署名)でグルーピングして、部署名でソートをおこなっています。
【関連記事】
▶SQL distinctのサンプルコード集 group byよりも700倍速い?
countで複数テーブルの件数を1つのSQLでカウントするサンプルコード
異なる複数のテーブルの件数を、countとサブクエリを使って1つのSQLでカウントできます。
select * from (select count(*) as employees_cnt from employees) a, (select count(*) as departments_cnt from departments) b, (select count(*) as salaries_cnt from salaries) c
employees(社員テーブル)の件数をemployees_cnt、departments(部署テーブル)の件数をdepartments_cnt、salaries(年収テーブル)の件数をsalaries_cntと別名定義し、取得しています。サブクエリを一つのテーブルとしてみなす際、同一テーブルとみなされないよう、テーブル名をa,b,cと別名定義しています。
実行するとこうなります。
countで重複数を条件にデータを取得するサンプルコード
havingで条件指定することで、count関数の結果をクエリの条件に組み込むことができます。
select hire_date,count(*) from employees group by hire_date having count(*) > 125
上記SQLは、employees(社員テーブル)から、同じhire_date(雇用日)が125件以上あるデータの、hire_date(雇用日)と件数を取得します。
実行するとこうなります。
実行順序による制約から、countに対する条件はwhere句で指定できません。count、sum、avgなどの集約関数の条件はhavingで指定します。
count(1)と記述しても、実行速度は速くならない
昔のSQLコードには、count(*)とすべきところを、count(1)と記述していることがあります。
当時のデータベースは最適化機能が今ほど高性能ではなかったため、count(1)を記述することで多少の速度アップが図れたんですね。
最近のデータベースでは、count(*)とcount(1)の実行速度は同じです。
コードのメンテナンス時など、他者に無用の混乱を与える場合がありますので、count(1)という記述は避けましょう。
countを高速化するには、管理テーブルを利用
count(*)でテーブル件数を数えると、テーブルフルスキャンが実行されるため、件数が多くなるほど処理時間がかかります。
数千万件、数億件ともなると件数を取得するだけでも10分以上かかるケースがあるんですね。
DBMSによっては、管理テーブルを直接参照することでテーブルの件数を取得できます。
例えば、MySQLの場合は、以下のSQLを実行すると、指定したテーブルの全件数が一瞬で取得できます。
select table_rows from information_schema.tables where table_name = 'employees'
実行すると、こうなります。
ただし、この数値は、実際の件数とズレることがあり、内部で統計的に算出しているようです。厳密な件数ではなく、ざっくりした値が取りたいときに使うと良いでしょう。
また、SQL Serverでも、システム・ストアドプロシジャのsp_spaceusedを使って、短い時間でテーブル件数が取得できるという情報がありました。
SQL Serverで大量レコードの件数を取得する | OISブログ
まとめ
- 同一テーブルの異なる条件のレコード数カウントには、count(条件 or NULL)を使う
- グループごとのレコード数カウントには、countとgroup byを組み合わせる
- データの種類数カウントには、countとdistinctを組み合わせる
- 複数テーブルの件数を1クエリで取得するには、countとサブクエリを組み合わせる
- countの結果をクエリの条件に組み込むにはhavingを使う
- count(1)とcount(*)の実行速度は変わらない
- 管理テーブルを直接参照で、大量件数カウントが一瞬でできるケース有り