前回に複数のSQL文の結果をうまく1つのSQL文にまとめる話をしました。今回もその続きです。
有効と無効のそれぞれの商品数
実行して知りたい結果は、有効な商品と無効な商品のそれぞれの商品数です。
SQL文のwhereで条件を指定すれば、
mysql> select count(*) from product where active_flag = 'Y';
+----------+
| count(*) |
+----------+
| 9492 |
+----------+
1 row in set (0.01 sec)
mysql> select count(*) from product where active_flag = 'N';
+----------+
| count(*) |
+----------+
| 4065 |
+----------+
1 row in set (0.01 sec)
と2つのSQL文を実行します。
しかし、これを1つのSQL文にすると、
mysql> select count(active_flag = 'Y') as 有効な商品数, count(active_flag = 'N') as 無効な商品数 from product;
+--------------------+--------------------+
| 有効な商品数 | 無効な商品数 |
+--------------------+--------------------+
| 13557 | 13557 |
+--------------------+--------------------+
1 row in set (0.02 sec)
結果おかしいですね。有効も無効も同じ商品数となってしまいました。しかも両方ともすべての商品数です。
COUNT()関数の引数
SQL文のCOUNT()関数では、その引数の値がNULLでないときにカウントします。
例えば、Laravelのデフォルトのプロジェクトで作成されるDBテーブルのusersに対して、以下のようにクエリーすると、
select count(remember_token) from users
ログインで「次回から自動ログインする」(Remember Me?)のチェックボックスをオンにしてログインしたユーザーのみのレコード数を得られます。なぜなら、remember_tokenはレコード作成時にはNULLの値であり、先のようにログインしたときのみにremember_tokenにランダムの文字列が入ります。
先の、有効も無効も結果が同じ商品数となったケースは、以下のように、引数に指定するExpressionは0あるいは1の値となり、NULLの値とはなりません。それゆえに、それらの引数でCOUNT()すると全レコードが対象となります。
mysql> select (active_flag = 'Y'), (active_flag = 'N') from product where active_flag = 'Y' limit 1;
+---------------------+---------------------+
| (active_flag = 'Y') | (active_flag = 'N') |
+---------------------+---------------------+
| 1 | 0 |
+---------------------+---------------------+
1 row in set (0.00 sec)
ちなみに、引数にを指定するとき、つまりの馴染みのCOUNT()では、レコードにNULLの値が含まれても全レコードを返します。
条件付きでレコードをカウントするには
さて、それではどう条件付きをカウントしたらよいでしょうか?
1つは先のExpressionが返す値を利用して、合計を計算するSUM()の関数を利用できます。
mysql> select sum(active_flag = 'Y') as 有効な商品数, sum(active_flag = 'N') as 無効な商品数 from product;
+--------------------+--------------------+
| 有効な商品数 | 無効な商品数 |
+--------------------+--------------------+
| 9492 | 4065 |
+--------------------+--------------------+
1 row in set (0.02 sec)
あるいは、IF()の関数で返す値を非NULLあるいはNULLとしてからカウントします。
mysql> select count(if(active_flag = 'Y', 1, null)) as 有効な商品数, count(if(active_flag = 'N', 1, null)) as 無効な商品数 from product;
+--------------------+--------------------+
| 有効な商品数 | 無効な商品数 |
+--------------------+--------------------+
| 9492 | 4065 |
+--------------------+--------------------+
1 row in set (0.01 sec)
ごちゃごちゃしているなら、以下のようにシンプルにもできます。
mysql> select count(active_flag = 'Y' or null) as 有効な商品数, count(active_flag = 'N' or null) as 無効な商品数 from product;
+--------------------+--------------------+
| 有効な商品数 | 無効な商品数 |
+--------------------+--------------------+
| 9492 | 4065 |
+--------------------+--------------------+
1 row in set (0.01 sec)