SQLのcount(<条件>) と三値論理
SQLのcount(<条件>) と三値論理
よくレコードの数を数えるために count(*)
を使うと思います。
例えばこのようなテーブルとレコードがあったとします。
test_results テーブル
user_id | score |
---|---|
1 | 90 |
2 | 70 |
3 | 50 |
4 | 30 |
5 | 20 |
6 | NULL |
test_resultsの数を取得するには
select count(*) from test_results
=> 6
実は count
は ()
の中に条件を書くことができます。
ためしに50点以上のレコードを取得してみましょう
select count(score >= 50) from test_results
=> 5
。。。あれ?「3」と返ってきてほしいのに「5」が返ってきてしましました。
これはなぜでしょうか?
それは count(<条件>)はNULL以外を数え上げる となっているからです。
https://www.postgresql.jp/document/12/html/functions-aggregate.html
countを使わずにselectすると以下のようになります。
select score >=50 from test_results
=>
TRUE
TRUE
TRUE
FALSE
FALSE
NULL
6行目以外はTRUE/FALSEが返ってきているので5となります。
では、狙い通りに50点以上のレコードの数を数えるにはどうすればいいのでしょうか?
ここで出てくるのが三値論理です
三値論理
TRUEとFALSEの2つを使っての論理を判定するのが二値論理です。
例えば TRUE かつ FALSE ならば FALSEといったものですね。
これはプログラマの方ならよく扱っているかと思います。
SQLではNULLという概念があり、TRUEとFALSEにNULLを加えた3つの値を扱うので三値論理を用います。
NULLとは不明や不定といった概念です。空ではありません。
NULLを含めた真理表はこちらなどから確認できます
https://www.postgresql.jp/document/12/html/functions-logical.html
しかし私にはぱっとわかりませんでした。
そこで自分なりにイメージをしてみたところしっくり来たのでいくつか紹介したいと思います。
TRUE and NULL
A:
B:
上の画像を見ての命題が「AがグレープフルーツかつBがグレープフルーツである」だったらどうなるでしょう?
AはグレープフルーツなのでTRUEですね。
しかしBはどうでしょう?中身がわからないのでグレープフルーツかもしれないし、りんごかも知れない。
つまり不明で判断がつかないですね。
なのでこの命題は「不明」なのでNULLになります。
TRUE or NULL
A:
B:
同じ図を使います。
このときの命題が「AがグレープフルーツまたはBがグレープフルーツである」だったらどうなるでしょう?
AはグレープフルーツなのでTRUEですね。
一方Bは先程も説明した通り不明で判断がつかないですね。
しかしAがグレープフルーツで確定しているのでこの命題はTRUEとなります。
FALSE or NULL
A:
B:
今度はAがりんごになりました。このときの命題が「AがグレープフルーツまたはBがグレープフルーツである」だったらどうなるでしょう?
AはりんごでグレープフルーツではないのでFALSEですね。
Bは不明です。りんごかもしれないし、グレープフルーツかもしれない。
AがFALSEですが、Bが判断つかないのでこの命題はNULLになります。
FALSE and NULL
A:
B:
同じ図を使います。
このときの命題が「AがグレープフルーツかつはBがグレープフルーツである」とします。
AはりんごでグレープフルーツではないのでFALSEですね。
Bは不明です。りんごかもしれないし、グレープフルーツかもしれない。
Bが判断つきませんが、AがFALSEの時点でこの命題は成立しないのでFALSEになります。
count(<条件>) と三値論理
ここでようやく本題に戻ってきました。
先程 count(<条件>)はNULL以外を数え上げる とお伝えしました。
なので「カウントしたくないものはNULLにしてあげればよい」ということになります。
それには三値論理を使います。
条件に合わないものをNULLにしたいので、FALSE or NULL になるようにすればよいですね。
ということで
test_results テーブル
user_id | score |
---|---|
1 | 90 |
2 | 70 |
3 | 50 |
4 | 30 |
5 | 20 |
6 | NULL |
というテーブルから50点以上のレコードを取得したいときは以下の様に記述すればOKです。
select count(score >= 50 or NULL) from test_results
=> 3
狙い通りの集計ができました!
countを外すと以下のようになります。
select score >= 50 or NULL from test_results;
=>
TRUE
TRUE
TRUE
NULL
NULL
NULL
さいごに
集計はSQLの得意分野です。アプリケーションでゴリゴリ計算せず、得意なレイヤーに任せることでアプリケーション全体の高速化に繋がります。
ぜひお試しください。
それではまた!