WHERE句の基礎はこれで習得!ビジネスパーソンのためのSQL入門講座

SQLを使ってデータを抽出する時、抽出対象のレコードを抽出するために指定するのが「WHERE」句です。
ビジネスの場面では、WHERE句を使わないクエリを使うことはほぼ無いでしょう。
何かしらの絞り込みを指定することがほとんどです。
しっかり使い方をマスターしておきましょう。

初心者はWHERE句で2つのポイントをおさえればOK!

  • WHERE句で指定した条件式でTRUEになるレコードだけを抽出できる。
  • ビジネスパーソンが使う時は、沢山の条件をANDでつなぐことが多い。

最初に演習用サンプルデータを説明します

当記事の解説で利用するサンプルデータです。
SELECT句で試したように、魔法の呪文としてコピペしてご利用ください。

-- サンプルデータ -- ここから
WITH sample AS (
  SELECT * FROM UNNEST(ARRAY<STRUCT<account_id INT64, account_name STRING, birthday DATE, is_certified BOOL, is_deleted BOOL>>[
    (1, "taro", DATE("2000-01-01"), TRUE, FALSE),
    (2, "hanako", DATE("1999-12-31"), TRUE, FALSE),
    (3, "pochi", CAST(NULL AS DATE), FALSE, FALSE),
    (4, "mike", DATE("2010-10-10"), TRUE, FALSE),
    (5, "yukichi", DATE("1835-01-10"), TRUE, TRUE)
  ])
)
-- サンプルデータ -- ここまで
account_idaccount_namebirthdayis_certifiedis_deleted
1taro2000/01/01TRUEFALSE
2hanako1999/12/31TRUEFALSE
3pochiNULLFALSEFALSE
4mike2010/10/10TRUEFALSE
5yukichi1835/01/10TRUETRUE

アカウント情報を格納したテーブルで、birthday(誕生日)、is_certified(認証済フラグ)、is_deleted(削除済フラグ)のカラムを用意しています。

WHERE句を使うと抽出レコードを絞ることができる

SELECT句では抽出したいカラムを選択できました。
FROM句では参照するデータソースを選択できました。
WHERE句では、抽出したいレコードを指定することができます。

WHERE句を使えるようになれば、FROM句で指定したデータソースから、欲しいレコードの欲しいカラムだけを抽出できるようになります。

WHERE句の後ろには条件式を書く

SELECT句では、「SELECT」と書いた後に抽出したいカラムを入力しました。
FROM句では、「FROM」と書いた後に参照したいデータソースを入力しました。
WHERE句では、「WHERE」と書いた後に条件式を入力します。

WHERE句の後ろに書いた条件式は、参照データソースのレコード1行毎に判定が行われます。
たとえば参照データソースに3行のレコードがあれば、1行目の条件式判定結果、2行目の条件式判定結果、3行目の条件式判定結果があるわけです。
その中で、条件式判定結果がTRUEだったレコードだけが抽出されるようになっています。
FALSEになったレコードは抽出対象外となります。

実務でIDを指定してデータを抽出する場面は多い

実務の場面では、何か特徴的なレコードを抽出する際、ID(プライマリキー)が分かっていて、それを使って抽出する場面によく遭遇します。
たとえば、「account_idが2のユーザさんから、ログインできない旨の問い合わせがあったから調べて」、といった具合です。
その時に使うWHERE句を例に試してみましょう。

-- 演習01
SELECT * FROM sample
WHERE account_id = 2

実際にクエリを実行した結果がこちら。
account_idが「2」のレコードだけが抽出されています。

WHERE句の後ろの条件式「account_id = 2」が判定されています。
1レコードずつ、各レコードのaccount_idカラムを見て、「2」と一致するか一致しないかを評価して、TRUEになったレコードだけを抽出し、TRUEにならなかったレコードは抽出しないようになっています。

今回のサンプルデータは5レコードのみですが、実際の業務では、何万何億のレコードの中から抽出することになります。
抽出対象が複数ある時はIN句を使います。

-- 演習02
SELECT * FROM sample
WHERE account_id IN (1, 2)

実際にクエリを実行した結果がこちら。
account_idが「1」と「2」のレコードだけが抽出されています。

WHERE句の後ろの条件式「account_id IN (1, 2)」が判定されています。
1レコードずつ、各レコードのaccount_idカラムを見て、「1」か「2」と一致するか一致しないかを評価して、TRUEになったレコードだけを抽出し、TRUEにならなかったレコードは抽出しないようになっています。

さらに実践的な場面を想定すると、「JOIN句」や「副問い合わせ」についての理解が必要になります。
それが使えると、抽出対象が沢山存在するケースにも対応できます。
初心者は勉強を後回しにしましょう。

JOIN句を解説する記事へのリンクを設置予定

副問い合わせを解説する記事へのリンクを設置予定

また、「プライマリキー」という単語が登場していますが、初心者はこれも勉強を後回しにしましょう。

プライマリキーを解説する記事へのリンクを設置予定

何かの特徴を持つレコードを抽出する場面でも使う

実務でデータ分析する時、「こういう特徴を持つユーザはどれくらい存在するだろうか」と、特徴を基準に抽出する場面がよくあります。
たとえば、「10歳以上、30歳以下のユーザがどれくらいいるか調べて」、といった具合です。

次の演習では、条件を少し簡単にし、誕生日が1993/01/01から2013/01/01の間のアカウントを抽出するようにしています。

-- 演習03
SELECT * FROM sample
WHERE birthday BETWEEN DATE("1993-01-01") AND DATE("2013-01-01")

実際にクエリを実行した結果がこちら。

WHERE句の後ろの条件式「birthday BETWEEN DATE(“1993-01-01”) AND DATE(“2013-01-01”)」が判定されています。
1レコードずつ、birthdayカラムを見て評価し、TRUEになったレコードだけを抽出し、TRUEにならなかったレコードは抽出しないようになっています。

ちなみに、条件式の中で計算もできるため、正確に誕生日を把握し利用する場面では下記のようになります。
初心者には難しいでしょうから、こんなこともできるようになる、くらいの感覚で見てみましょう。

-- 演習04
SELECT *,
  CAST(FLOOR((CAST(FORMAT_DATE("%Y%m%d", CURRENT_DATE("Asia/Tokyo")) AS INT64) - CAST(FORMAT_DATE("%Y%m%d", birthday) AS INT64)) / 10000) AS INT64) AS age
FROM sample
WHERE CAST(FLOOR((CAST(FORMAT_DATE("%Y%m%d", CURRENT_DATE("Asia/Tokyo")) AS INT64) - CAST(FORMAT_DATE("%Y%m%d", birthday) AS INT64)) / 10000) AS INT64) BETWEEN 10 AND 30

実際にクエリを実行した結果がこちら。
「副問い合わせ」などを使うことで、もっとキレイなクエリになるのですが、今回は利用しない書き方にしています。

BOOL型でレコードを絞る場面もよくある

たとえば、「キャンペーン対象のアカウントの一覧が欲しいから、認証済のアカウントだけ抽出して」といった指示があったとしましょう。

-- 演習05
SELECT * FROM sample
WHERE is_certified

実際にクエリを実行した結果がこちら。

条件式を処理した結果はBOOL型になるので、BOOL型のカラムを使えば、カラム名だけ書く形でOKです。
「is_certified = TRUE」のように明示的に書いても良いのですが、慣れると面倒に感じるようになるでしょう。

もう一つ例を挙げ、「未削除のアカウントの一覧を抽出して」といった指示を想定してみましょう。

-- 演習06
SELECT * FROM sample
WHERE NOT is_deleted

実際にクエリを実行した結果がこちら。

条件式「NOT is_deleted」が評価され、is_deletedがFALSEのレコードが評価結果TRUEになり、抽出されています。

実務では沢山の条件をANDでつなぐ場面が多い

実務では、もっと複雑な条件で抽出を要求されることが多いです。
「キャンペーン対象の条件は、20歳以上で未削除・認証済のアカウントにする」といった場合を例にしてみます。
ただし、「20歳以上」の条件を簡略化し、「誕生日が2003/01/01より前」と解釈します。

SELECT * FROM sample
WHERE birthday < DATE("2003-01-01")
AND is_certified
AND NOT is_deleted

実際にクエリを実行した結果がこちら。

条件式「birthday < DATE(“2003-01-01”) AND is_certified AND NOT is_deleted」が評価され、TRUEになったレコードのみが抽出されています。
実務では「AND」(かつ)を使うことが多く、WHERE句では「AND」毎に改行する人が多いです。
この書き方には慣れておきましょう。

WHERE句が無いクエリは「WHERE TRUE」と書くのと同じ結果になる

余談ですが、「WHERE TRUE」と書いた場合、1レコードずつ評価し、どのレコードでもTRUEが返されるため、全てのレコードが抽出されることになります。
WHERE句を省略した時と同じ結果になります。

SELECT * FROM sample
WHERE TRUE

実際にクエリを実行した結果がこちら。

条件式の結果がNULLになっているレコードは抽出されない

WHERE句で指定した条件式をレコード毎に判定し、TRUEのレコードだけが抽出され、FALSEのレコードは抽出されません。
ではNULLはどうなるかというと、SQLでは抽出対象外の扱いとなります。

抽出されてほしいレコードが抽出されない、といった想定外の挙動(不具合)につながるので、条件式を書く時には必ず、「この条件式はNULLになることはあるんだろうか」と気配りする必要があります。
よくある不具合なので覚えておきましょう。

条件式でNULLが登場した時はIFNULL

「もしNULLだったら」の条件分岐にはIFNULL関数を使えます。
適切に使って、NULLだった時の代替値を設定しましょう。
ただし、条件式の結果がNULLになった時は、元々取り扱っているデータ自体が想定外だった可能性があるため、どのように修正するのかは熟考する必要があります。

「WHERE TRUE」から書き始める人もいる

TRUEならどのレコードも絞っていないのではないか、と疑問に思われる方もいらっしゃるでしょうから、そういう書き方もあることをご紹介しておきます。

クエリを書きながら試行錯誤していると、WHERE句で色々な条件の組み合わせを試す場面に遭遇します。
条件1かつ条件2かつ条件3を試した後に、やっぱり条件2かつ条件3で試してみよう、といった具合です。

WHERE 条件1
AND 条件2
AND 条件3

その時こんな書き方をしていると、1行目を消した後に2行目の「AND」を「WHERE」に書き換えることになります。

WHERE 条件2
AND 条件3

「WHERE TRUE」で書き始める書き方では、「AND」を「WHERE」に書き換える手間がなくなります。

WHERE TRUE
AND 条件1
AND 条件2
AND 条件3

2行目を消すだけで、「AND」を「WHERE」に書き換える必要が無いわけです。

WHERE TRUE
AND 条件2
AND 条件3

必ずこの書き方をしなければならないというわけではないので、Tipsとして覚えておきましょう。

また、稀に「WHERE TRUE」でなく「WHERE 1 = 1」と書き始める人もいますが、条件式の判定結果はTRUEなので変わりないです。

抽出レコードを絞るのにLIMIT句を使う方法もある

用途は異なりますが、抽出レコードを絞る、という意味ではLIMIT句も同じ機能です。
WHERE句は条件に合うレコードのみを抽出するので利用場面が多いですが、LIMIT句ではレコード数で絞るため、非エンジニアでの利用場面があまりありません。

詳しくはORDER BY句で解説するので、そちらをご一読ください。

ORDER BY句を解説する記事へのリンクを設置予定

振り返り

お疲れさまでした。
条件式をしっかり勉強した後であれば、そんなに苦労せず勉強できたのではないでしょうか。

実のところ、条件式をWHERE句専用の書き方だと勉強してしまうと、WHERE句で覚えることを多く感じてしまったり、条件式を応用したクエリを見た時に混乱してしまい、苦手意識を持ってしまうことがあります。
条件式の勉強を読み直される場合は、下記リンクからご覧ください。

条件式を解説する記事へのリンクを設置予定

WHERE句をマスターした次は、「ORDER BY」句です。
下記リンクから読み進めてください。

ORDER BY句を解説する記事へのリンクを設置予定