前回に複数の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)メルマガ購読の申し込みはこちらから。