WHEREと条件|テーブル・SELECT・条件抽出

このページはプロモーション(広告)を含みます。

テーブル・SELECT・条件抽出(初級・読む25分+演習35分)

この授業のゴール

仕事でどう使うか

WHERE は、どの行を結果に入れるかを決める条件です。ここを誤ると、「4月の売上」に3月や5月の注文が混ざったり、取消注文が入ったりして、報告する数字そのものが変わります。

路線 この授業の使いどころ
DS(データサイエンティスト) 分析対象の期間・状態を条件で固定する。「4月・支払済み」など対象を明記する
ML(機械学習エンジニア) 学習期間と評価期間を条件で分ける。期間の境界がずれると、未来の情報が学習に混ざる
DX(DX推進・AI企画) KPI定義書の「集計対象」を、そのままWHERE条件に翻訳する。仕様書と条件の対応を確認できる
RS(研究者) 採択基準(対象者の条件)を条件式で明記し、再現できる形にする

実務では「期間」「状態(取消・返金)」「チャネル」など複数の条件を組み合わせます。そのとき、ANDとORの優先順位と期間の境界が、最も多い誤りの原因です。

前提

次の言葉を使います。不安があれば先に戻ってください。

この授業では、SELECTと列 と同じ注文表 orders(8行)を使います。

身近な状況:「4月の支払済み注文」は何件?

経理から「4月の支払済み注文の件数と注文IDがほしい」と頼まれました。注文表 orders(1行=注文1件)の内容は次のとおりです。

order_id order_date status channel amount
1 2025-03-31 paid web 3000
2 2025-04-01 paid app 1200
3 2025-04-01 cancelled web 2500
4 2025-04-10 paid store 0
5 2025-04-15 refunded app 1800
6 2025-04-20 paid web NULL
7 2025-04-30 paid web 1200
8 2025-05-01 paid app 4000

目で見ると、4月の支払済みは注文2、4、6、7の4件です。注文1は3月末、注文8は5月初めで期間の外、注文3は取消、注文5は返金で状態が違います。

3月31日と5月1日の注文が、期間の境界ぎりぎりに置かれているのは意図的です。条件を書くとき、境界のデータが入るか入らないかを、自分で確かめる習慣が必要です。

直感的な説明:行にふるいをかける

WHERE はふるいです。表の各行を1行ずつ条件に通し、真(条件に合う)になった行だけが残ります。

SQLは、基本的に次の順序で処理します。

  1. FROM 表を決める
  2. WHERE 行を絞る
  3. SELECT 列を選ぶ

表を決めたあとに行を絞り、最後に列を選ぶ、と覚えておくと、「WHERE で SELECT の別名を使えるか」といった疑問が整理できます(SQLiteでは使えますが、他のデータベースでは使えないことが多いので、別名は WHERE に使わないのが安全です)。

正確な定義:比較演算子・AND/OR・半開区間

比較演算子:

書き方 意味 備考
= 等しい 文字列は 'paid' のように一重引用符で囲む
<>(または !=) 等しくない NULLには効かない(NULLと3値論理)
>、>=、<、<= 大小 文字列は辞書順、日付文字列 'YYYY-MM-DD' は日付順
IN ('a','b') リストのどれかと等しい = 'a' OR = 'b' と同じ
BETWEEN a AND b a以上b以下(両端を含む) 日時が混ざると取りこぼしやすい
LIKE 'SP%' パターン一致。% は任意の文字列 大文字小文字の扱いは方言による

AND と OR の優先順位:AND は OR より先に評価されます(掛け算が足し算より先、と同じ)。

$$A \land B \lor C = (A \land B) \lor C$$

ここで $\land$ は「かつ(AND)」、$\lor$ は「または(OR)」です。たとえば status = 'paid' AND channel = 'web' OR channel = 'app' は、「(支払済み かつ web) または (app)」と読まれます。意図が「支払済み かつ (web または app)」なら、括弧が必須です。

半開区間:期間は「以上(>=)開始日、未満(<)翌期間の開始日」で書きます。4月なら

$$\text{'2025-04-01'} \le \text{order\_date} < \text{'2025-05-01'}$$

終了日を <= '2025-04-30' と書く方法は、時刻付きのデータ('2025-04-30 18:00:00')で4月30日の注文を取りこぼします。< '2025-05-01' なら、日付だけのデータでも時刻付きでも正しく動きます(型・日付・式 で詳しく扱います)。

段階付き例題:4月の支払済み注文を抽出し、検算する

問題:4月(2025-04-01以上2025-05-01未満)の支払済み注文のID・日付・金額を、IDの昇順で出す。

ステップ1:条件を言葉にする。「状態が paid」かつ「日付が4月」。

ステップ2:条件を式にする。状態は status = 'paid'。日付は半開区間で order_date >= '2025-04-01' AND order_date < '2025-05-01'。3つの条件はすべて AND でつなぐ。

ステップ3:実行する。

-- db: none
CREATE TABLE orders (
  order_id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL,
  order_date TEXT NOT NULL,
  status TEXT NOT NULL CHECK(status IN ('paid','cancelled','refunded')),
  channel TEXT NOT NULL,
  amount INTEGER,
  coupon_code TEXT
);
INSERT INTO orders VALUES
  (1,101,'2025-03-31','paid','web',3000,NULL),
  (2,102,'2025-04-01','paid','app',1200,'SPRING'),
  (3,101,'2025-04-01','cancelled','web',2500,NULL),
  (4,103,'2025-04-10','paid','store',0,NULL),
  (5,104,'2025-04-15','refunded','app',1800,'SPRING'),
  (6,102,'2025-04-20','paid','web',NULL,NULL),
  (7,105,'2025-04-30','paid','web',1200,''),
  (8,103,'2025-05-01','paid','app',4000,'VIP');
SELECT order_id, order_date, amount
FROM orders
WHERE status = 'paid'
  AND order_date >= '2025-04-01'
  AND order_date <  '2025-05-01'
ORDER BY order_id;

ステップ4:結果を読む。注文2、4、6、7の4行です。注文1(3月31日)と注文8(5月1日)は期間外、注文3(取消)と注文5(返金)は状態が違うので除かれます。

ステップ5:目視で検算する。元の8行を見て、4月の日付は2〜7の6件。そのうち paid は3(取消)と5(返金)を除く4件。結果と一致します。

ステップ6:金額列の意味を確認する。結果には金額が 1200、0、NULL、1200 があります。注文4は0円(無料)、注文6は未記録(NULL)です。この4件の金額の合計は 2400 になりますが、これは注文6を含まない合計です(SUM はNULLを無視します)。「4件の売上合計」と報告する前に、未記録の1件があることを併記します。

操作して理解する:ANDとORの優先順位と、境界の確認

次の3つのSQLを、実行する前に結果を予想してから確かめてください。

-- db: none
CREATE TABLE orders (
  order_id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL,
  order_date TEXT NOT NULL,
  status TEXT NOT NULL CHECK(status IN ('paid','cancelled','refunded')),
  channel TEXT NOT NULL,
  amount INTEGER,
  coupon_code TEXT
);
INSERT INTO orders VALUES
  (1,101,'2025-03-31','paid','web',3000,NULL),
  (2,102,'2025-04-01','paid','app',1200,'SPRING'),
  (3,101,'2025-04-01','cancelled','web',2500,NULL),
  (4,103,'2025-04-10','paid','store',0,NULL),
  (5,104,'2025-04-15','refunded','app',1800,'SPRING'),
  (6,102,'2025-04-20','paid','web',NULL,NULL),
  (7,105,'2025-04-30','paid','web',1200,''),
  (8,103,'2025-05-01','paid','app',4000,'VIP');
-- (1) 括弧なし:AND が先
SELECT order_id FROM orders
WHERE status = 'paid' AND channel = 'web' OR channel = 'app'
ORDER BY order_id;
-- (2) 括弧あり:意図どおり「支払済み かつ (web または app)」
SELECT order_id FROM orders
WHERE status = 'paid' AND (channel = 'web' OR channel = 'app')
ORDER BY order_id;
-- (3) BETWEEN は両端を含む
SELECT order_id FROM orders
WHERE order_date BETWEEN '2025-04-01' AND '2025-04-30'
ORDER BY order_id;

答えと読み方:

(1) と (2) の差が、「括弧を付けるか否か」で生じる誤りの典型です。ANDとORを混ぜたら、必ず括弧を付けるルールにしましょう。

試してみよう:(1) の条件を channel = 'app' OR channel = 'web' AND status = 'paid' に並べ替えても結果は同じです。AND が先に評価されるため、書く順番は関係しません。

発展:NULLを含む列への条件と、BETWEENの落とし穴

NULLを含む列への否定条件:coupon_code <> 'SPRING' は「クーポンがSPRINGでない注文」のつもりで書いても、本文のデータでは注文7(空文字)と8(VIP)の2件だけが返り、クーポンなし(NULL)の注文1、3、4、6は返りません。NULLとの比較は「真」にならないためです。「クーポンなしも含めたい」なら coupon_code IS NULL OR coupon_code <> 'SPRING' と書きます(NULLと3値論理 で詳しく学びます)。

BETWEEN と時刻:BETWEEN '2025-04-01' AND '2025-04-30' は、'2025-04-30 18:30:00' のような値を含みません(文字列として '2025-04-30 18:30:00' は '2025-04-30' より大きいため)。日付型のみのデータでは問題が表に出ないため、データの形式が変わったとき(システム更新で時刻付きになったとき)に、静かに数字が減ります。半開区間にしておくと、この変更に強くなります。

IN とサブクエリ:WHERE customer_id IN (SELECT ...) のように、IN の中に別の問い合わせを書くこともできます(「サブクエリ」(有料版))。リストにNULLが含まれる NOT IN は想定外の結果になるため、NULLと3値論理 で扱います。

LIKE の注意:LIKE 'SP%' は先頭が SP で始まる文字列に一致します。% が先頭にあると(LIKE '%ING')、索引(検索を速くする仕組み)が使えず、大きな表では遅くなります。大文字小文字の扱いもデータベースによって異なります。

よくある失敗

失敗1:OR を括弧なしで混ぜて、対象外の行が入る

依頼:「支払済みで、チャネルが web または app の注文」。

-- db: none
CREATE TABLE orders (
  order_id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL,
  order_date TEXT NOT NULL,
  status TEXT NOT NULL CHECK(status IN ('paid','cancelled','refunded')),
  channel TEXT NOT NULL,
  amount INTEGER,
  coupon_code TEXT
);
INSERT INTO orders VALUES
  (1,101,'2025-03-31','paid','web',3000,NULL),
  (2,102,'2025-04-01','paid','app',1200,'SPRING'),
  (3,101,'2025-04-01','cancelled','web',2500,NULL),
  (4,103,'2025-04-10','paid','store',0,NULL),
  (5,104,'2025-04-15','refunded','app',1800,'SPRING'),
  (6,102,'2025-04-20','paid','web',NULL,NULL),
  (7,105,'2025-04-30','paid','web',1200,''),
  (8,103,'2025-05-01','paid','app',4000,'VIP');
-- 誤り:AND が先に評価される
SELECT order_id, status, channel FROM orders
WHERE status = 'paid' AND channel = 'web' OR channel = 'app'
ORDER BY order_id;

注文5(refunded・app)が入ります。「paid かつ web」または「app」と読まれたためです。直し方:status = 'paid' AND (channel = 'web' OR channel = 'app')、または status = 'paid' AND channel IN ('web', 'app') と書く。件数を元の表で目視確認する。

失敗2:BETWEEN や <= で月末・月初の境界を誤る

依頼:「4月の注文」。担当者が BETWEEN '2025-04-01' AND '2025-05-01' と書いたとします(5月1日を含めてしまった例)。

-- db: none
CREATE TABLE orders (
  order_id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL,
  order_date TEXT NOT NULL,
  status TEXT NOT NULL CHECK(status IN ('paid','cancelled','refunded')),
  channel TEXT NOT NULL,
  amount INTEGER,
  coupon_code TEXT
);
INSERT INTO orders VALUES
  (1,101,'2025-03-31','paid','web',3000,NULL),
  (2,102,'2025-04-01','paid','app',1200,'SPRING'),
  (3,101,'2025-04-01','cancelled','web',2500,NULL),
  (4,103,'2025-04-10','paid','store',0,NULL),
  (5,104,'2025-04-15','refunded','app',1800,'SPRING'),
  (6,102,'2025-04-20','paid','web',NULL,NULL),
  (7,105,'2025-04-30','paid','web',1200,''),
  (8,103,'2025-05-01','paid','app',4000,'VIP');
-- 誤り:5月1日の注文8が入る
SELECT order_id, order_date FROM orders
WHERE order_date BETWEEN '2025-04-01' AND '2025-05-01'
ORDER BY order_id;
-- 正しい:半開区間
SELECT order_id, order_date FROM orders
WHERE order_date >= '2025-04-01' AND order_date < '2025-05-01'
ORDER BY order_id;

誤りの結果は注文2〜8の7件、正しい結果は注文2〜7の6件です。BETWEEN は両端を含むため、終了側に翌月の1日を書くと翌月の1日が混ざります。直し方:期間は常に「>= 開始日 AND < 翌期間の開始日」で書く。

失敗3:NULLを含む列の大小条件で、NULLの行を取りこぼす

依頼:「金額が1200円以上の注文」。amount >= 1200 と書くと、本文のデータで注文 1、2、3、5、7、8 が出ます。一方、注文4(0円)と注文6(NULL)は出ません。

-- db: none
CREATE TABLE orders (
  order_id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL,
  order_date TEXT NOT NULL,
  status TEXT NOT NULL CHECK(status IN ('paid','cancelled','refunded')),
  channel TEXT NOT NULL,
  amount INTEGER,
  coupon_code TEXT
);
INSERT INTO orders VALUES
  (1,101,'2025-03-31','paid','web',3000,NULL),
  (2,102,'2025-04-01','paid','app',1200,'SPRING'),
  (3,101,'2025-04-01','cancelled','web',2500,NULL),
  (4,103,'2025-04-10','paid','store',0,NULL),
  (5,104,'2025-04-15','refunded','app',1800,'SPRING'),
  (6,102,'2025-04-20','paid','web',NULL,NULL),
  (7,105,'2025-04-30','paid','web',1200,''),
  (8,103,'2025-05-01','paid','app',4000,'VIP');
SELECT order_id, amount FROM orders WHERE amount >= 1200 ORDER BY order_id;
-- 「金額が1200円未満」を求めたつもりで書く
SELECT order_id, amount FROM orders WHERE amount < 1200 ORDER BY order_id;

2つ目の結果は注文4(0円)だけです。注文6(金額NULL)が出ません。「1200以上」と「1200未満」を足しても、全8件にならず、注文6が漏れます。NULLは大小の比較で真にも偽にもならないためです。直し方:条件が漏れなく排反(重ならず、足すと全部)になっているか、件数 6+1+1=8 で確認する。NULLを含む列には IS NULL の条件を明示する(NULLと3値論理)。

まとめ

用語

WHERE(WHERE)

結果に残す行を選ぶ条件。条件が真になった行だけが残る。

半開区間(half-open interval)

開始を含み終了を含まない範囲。日付なら「開始日以上、翌期間の開始日未満」。

論理演算子(logical operator)

AND(かつ)、OR(または)、NOT(否定)。ANDはORより先に評価される。

比較演算子(comparison operator)

=、<>、<、>、<=、>= など、2つの値を比べて真偽を返す記号。

よくある質問

WHERE と HAVING の違いは?

WHERE は集計の前に行を絞り、HAVING は集計の後に集計結果(件数・合計)を絞ります。この授業では集計を使わないので WHERE だけです。HAVING は 「HAVINGとWHERE」(有料版) で扱います。

= と == のどちらを使いますか?

SQLでは比較に = を1つだけ使います。代入の意味はなく、「等しいか」の判定です。== もSQLiteでは使えますが、ほとんどのデータベースでは使えないので、= に統一するのが安全です。

文字列の引用符は一重と二重のどちらですか?

文字列の値は一重引用符 'paid' です。二重引用符は列名や表名を囲む用途です。SQLiteは二重引用符を文字列として解釈することがありますが、他のデータベースではエラーになるため、値は一重引用符にします。

期間の条件を `<= 月末日` ではなく `< 翌月1日` で書くのはなぜですか?

月末日が28日・30日・31日と一定でなく、データが時刻付きになると月末日の注文が漏れるからです。翌月1日の0時より前、と考えれば、月の長さや時刻形式に関係なく正しく動きます。

仕事で、条件が正しいかどうかをどう確かめますか?

(1) 小さな表で目視と一致するか、(2) 条件を足すごとに件数が減る方向に動くか、(3) 排反な条件の件数の合計が元の件数と一致するか、を確かめます。KPIの定義書に書かれた件数と照合するのも有効です。

LIKE と = の使い分けは?

完全一致なら =、先頭や一部の一致なら LIKE です。商品名の前方一致などで使いますが、表記ゆれ(全角・半角)を吸収するものではありません。値が確定しているコード類は = や IN を使うのが確実です。

確認問題

問1 WHERE status = 'paid' AND channel = 'web' OR channel = 'app' は、どのように評価されるか。

  1. ① 構文エラーになる
  2. ② status = 'paid' AND (channel = 'web' OR channel = 'app')
  3. ③ (status = 'paid' AND channel = 'web') OR channel = 'app'
  4. ④ OR が先に評価され、AND は無視される
解答と解説

正解:③

AND は OR より先に評価されます。そのため、app の注文は状態に関係なく入ります。

問2 2025年4月の注文を取り出す条件として、最も安全な書き方はどれか。

  1. ① order_date >= '2025-04-01' AND order_date < '2025-05-01'
  2. ② order_date > '2025-04-01' AND order_date < '2025-04-30'
  3. ③ order_date LIKE '2025-04'
  4. ④ order_date BETWEEN '2025-04-01' AND '2025-04-30'
解答と解説

正解:①

半開区間なら、時刻付きのデータでも月末の取りこぼしが起きません。

問3 BETWEEN 10 AND 20 はどの範囲を表すか。

  1. ① 10より大きく20以下
  2. ② 10より大きく20より小さい
  3. ③ 10以上20未満
  4. ④ 10以上20以下
解答と解説

正解:④

BETWEEN は両端を含みます。

問4 amount >= 1200 と amount < 1200 で取り出した2つの結果を足しても、表の全行にならない。考えられる理由はどれか。

  1. ① AND を使っていない
  2. ② amount が NULL の行がある
  3. ③ WHERE が二重に評価された
  4. ④ SELECT が列を減らした
解答と解説

正解:②

NULLは大小の比較で真にならないため、どちらの条件にも入りません。

問5 status IN ('cancelled', 'refunded') と同じ結果になる条件はどれか。

  1. ① status <> 'paid' AND status <> 'cancelled'
  2. ② status LIKE 'c%'
  3. ③ status = 'cancelled' AND status = 'refunded'
  4. ④ status = 'cancelled' OR status = 'refunded'
解答と解説

正解:④

IN はリストのどれかと等しい、という OR の短い書き方です。

問6 本文の注文表で、4月(2025-04-01以上2025-05-01未満)かつ paid の注文は何件か。

  1. ① 4件
  2. ② 5件
  3. ③ 6件
  4. ④ 3件
解答と解説

正解:①

注文2、4、6、7の4件です。3は取消、5は返金で除かれます。

次に学ぶこと

次は ORDER BYとLIMIT で、結果の並べ替え(ORDER BY)と上位N件の取得(LIMIT)を学びます。NULL の比較と3値論理は NULLと3値論理、日付や型は 型・日付・式 です。

この授業の確認問題だけで習得とはしません。モジュール テーブル・SELECT・条件抽出 の総合テストと自力課題で判定します。

キャリア学習:データサイエンティスト・機械学習エンジニア・DX推進・研究者を目指す人の学習アプリ。164モジュール・984授業を、ブラウザで動くSQLとPythonの演習で。基礎と各分野の最初のモジュールは無料。全部は2,980円の買い切り。

アプリを無料で開く有料版と無料版の違い