SQLのWHERE句で複数条件を書くときは、ANDとORでつなぎ、評価の順番は「かっこ > NOT > AND > OR」です。 ANDとORを混ぜるとANDが先にまとまるので、「AまたはB、かつC」と書きたいときは (A OR B) AND C のようにかっこで囲みます。かっこを付けるかどうかで、同じデータでも返ってくる件数が変わります。

この記事では、10行のサンプルデータに対して「かっこ無し」と「かっこ有り」のSQLを並べて実行し、件数がどう変わるかを確かめます。あわせて、NOT INとNULLで0件になる仕組み、NOT (A OR B) の書き換え(ド・モルガンの法則)、IN・BETWEEN・行値比較への書き換えを、PostgreSQL 16.15 と MySQL 8.0.46 の実行結果つきでまとめました。CREATE TABLE と INSERT はそのまま貼って試せます。

WHERE句の複数条件の早見表

やりたいこと 書き方 ポイント
両方を満たす行 WHERE A AND B 条件を足すほど件数は減る
どちらかを満たす行 WHERE A OR B 条件を足すほど件数は増える
「AかB」かつC WHERE (A OR B) AND C ORの側をかっこで囲む
Aではない行 WHERE NOT A NOTは直後の1条件だけに掛かる
「AかつB」ではない行 WHERE NOT (A AND B) まとめて否定するときはかっこ
いくつかの値のどれか WHERE col IN ('x', 'y', 'z') ORの連続を1つにできる
範囲で絞る WHERE col BETWEEN 500 AND 3000 両端を含む
列の組み合わせで絞る WHERE (a, b) IN (('x', 1), ('y', 2)) 行値比較。PostgreSQL・MySQLで実行確認
NULLの行を拾う WHERE col IS NULL col = NULL は0件になる
「〜以外」にNULLも含める WHERE col <> 'x' OR col IS NULL <> だけではNULLの行が外れる
リストに無い行 WHERE NOT EXISTS (...) NOT IN はリストにNULLがあると0件

優先順位は次の4段です。上にあるものほど先にまとまります。

順位 要素 例 読み方
1 かっこ ( ) (A OR B) AND C かっこの中が最優先
2 NOT NOT A AND B (NOT A) AND B
3 AND A OR B AND C A OR (B AND C)
4 OR A AND B OR C AND D (A AND B) OR (C AND D)

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL WHEREの複数条件|AND・ORの優先順位とかっこ・NOTの書き方」

検証環境とサンプルデータ(コピペ用)

実行結果はすべて次の環境で取りました。

項目 内容
PostgreSQL 16.15(psql で実行)
MySQL 8.0.46(mysql クライアントで実行)
Oracle・SQL Server 未実行。優先順位は各公式ドキュメントの表を開いて確認

備品の一覧を表す items テーブルを使います。color には NULL の行を2つ入れてあります。PostgreSQL と MySQL のどちらにもそのまま貼れます。

DROP TABLE IF EXISTS items;
CREATE TABLE items (
  id       INT PRIMARY KEY,
  name     VARCHAR(40) NOT NULL,
  category VARCHAR(20) NOT NULL,
  price    INT NOT NULL,
  stock    INT NOT NULL,
  color    VARCHAR(10)
);
INSERT INTO items (id, name, category, price, stock, color) VALUES
  (1, 'ノートA5',       '文具',  300, 120, '白'),
  (2, 'ボールペン',     '文具',  150,   0, '黒'),
  (3, '万年筆',         '文具', 3200,   8, '黒'),
  (4, 'デスク',         '家具', 18000,  3, '白'),
  (5, 'チェア',         '家具', 12000,  0, '黒'),
  (6, 'ブックエンド',   '家具',  800,  25, NULL),
  (7, 'USBハブ',        '家電', 2500,  14, '白'),
  (8, 'デスクライト',   '家電', 4800,   0, NULL),
  (9, '延長コード',     '家電',  900,  40, '白'),
  (10,'マウスパッド',   '雑貨',  600,  60, '赤');

AND・ORで複数条件をつなぐ基本

ANDは「すべて満たす」、ORは「どれかを満たす」 です。条件はいくつでもつなげます。

-- 家電で、かつ在庫が0
SELECT id, name FROM items WHERE category = '家電' AND stock = 0;

-- 300円か150円
SELECT id, name FROM items WHERE price = 300 OR price = 150 ORDER BY id;

2つ目のSQLの実行結果(PostgreSQL 16.15)です。

 id |    name    
----+------------
  1 | ノートA5
  2 | ボールペン
(2 rows)

ORでは、比較する列名を毎回書きます。price = 300 OR 150 のように列名を省いた書き方は、後半の「エラーになる例」で結果を確認します。

ANDとORを混ぜると件数が変わる(かっこ無し vs かっこ有り)

「文具か家具で、1000円以上のもの」を取りたいとします。日本語をそのまま並べると、次のSQLになります。

かっこ無し:5件

SELECT id, name, category, price FROM items
WHERE category = '文具' OR category = '家具' AND price >= 1000
ORDER BY id;
 id |    name    | category | price 
----+------------+----------+-------
  1 | ノートA5   | 文具     |   300
  2 | ボールペン | 文具     |   150
  3 | 万年筆     | 文具     |  3200
  4 | デスク     | 家具     | 18000
  5 | チェア     | 家具     | 12000
(5 rows)

300円のノートと150円のボールペンが入っています。ANDが先にまとまるので、このSQLは「文具」または「家具で1000円以上」と読まれたためです。文具には価格の条件が掛かっていません。

かっこ有り:3件

SELECT id, name, category, price FROM items
WHERE (category = '文具' OR category = '家具') AND price >= 1000
ORDER BY id;
 id |  name  | category | price 
----+--------+----------+-------
  3 | 万年筆 | 文具     |  3200
  4 | デスク | 家具     | 18000
  5 | チェア | 家具     | 12000
(3 rows)

ORの側をかっこで囲むと、ねらいどおり3件になりました。MySQL 8.0.46 でも同じ5件と3件です。

書き方 データベースの読み方 件数
A OR B AND C A OR (B AND C) 5件
(A OR B) AND C かっこのとおり 3件

データベースがどう読んだかを EXPLAIN で確かめる

優先順位に迷ったら、EXPLAIN の Filter 行を見ると、データベースが補ったかっこがそのまま表示されます。 PostgreSQL での実行結果です。

EXPLAIN (COSTS OFF)
SELECT * FROM items
WHERE category = '文具' OR category = '家具' AND price >= 1000;
 Seq Scan on items
   Filter: (((category)::text = '文具'::text) OR (((category)::text = '家具'::text) AND (price >= 1000)))

家具 と price >= 1000 がひとまとまりになっているのが読み取れます。MySQL 8.0.46 でも EXPLAIN FORMAT=TREE で同じ形を確認できました。

-> Filter: ((items.category = '文具') or ((items.category = '家具') and (items.price >= 1000)))

ORが2つ以上あるとき

ANDとORが増えても、考え方は同じです。ANDでつながった部分を先にひとかたまりにして、そのかたまり同士をORでつなぎます。

SELECT id, name FROM items
WHERE category = '家電' OR category = '家具' AND stock = 0 OR price < 200
ORDER BY id;
 id |     name     
----+--------------
  2 | ボールペン
  5 | チェア
  7 | USBハブ
  8 | デスクライト
  9 | 延長コード
(5 rows)

これは「家電」または「家具で在庫0」または「200円未満」の3つのかたまりです。家電は在庫があっても全部入ります。

かっこの位置で件数が変わる例をもう1組載せます。

SQL(WHERE以降) 件数
category = '家具' AND stock = 0 OR color IS NULL 3件
(category = '家具' AND stock = 0) OR color IS NULL 3件(上と同じ意味)
category = '家具' AND (stock = 0 OR color IS NULL) 2件

1行目と2行目が同じ件数なのは、もともとANDが先にまとまるからです。結果が変わらない場合でも、ANDとORを混ぜるときはかっこを書いておくと、読む人が優先順位を思い出す手間がなくなります。

NOTはどこまで掛かるか(2件と9件)

NOTはANDより先にまとまり、直後の1条件だけを否定します。 「文具で在庫0のもの以外」を取りたいときの2通りを比べます。

かっこ無し:2件

SELECT id, name, category, stock FROM items
WHERE NOT category = '文具' AND stock = 0
ORDER BY id;
 id |     name     | category | stock 
----+--------------+----------+-------
  5 | チェア       | 家具     |     0
  8 | デスクライト | 家電     |     0
(2 rows)

「文具ではない」かつ「在庫0」と読まれました。PostgreSQL の EXPLAIN でも Filter: (((category)::text <> '文具'::text) AND (stock = 0)) と表示され、NOTが category = '文具' だけに掛かっているのが分かります。

かっこ有り:9件

SELECT id, name, category, stock FROM items
WHERE NOT (category = '文具' AND stock = 0)
ORDER BY id;
 id |     name     | category | stock 
----+--------------+----------+-------
  1 | ノートA5     | 文具     |   120
  3 | 万年筆       | 文具     |     8
  4 | デスク       | 家具     |     3
  5 | チェア       | 家具     |     0
  6 | ブックエンド | 家具     |    25
  7 | USBハブ      | 家電     |    14
  8 | デスクライト | 家電     |     0
  9 | 延長コード   | 家電     |    40
 10 | マウスパッド | 雑貨     |    60
(9 rows)

在庫0の文具(ボールペン)だけが外れて9件になりました。複数の条件をまとめて否定したいときは、NOTの後ろをかっこで囲みます。

NOTを使う形は次のとおりです。演算子と組み合わせた形のほうが、掛かる範囲がはっきりします。

書き方 意味
NOT A Aではない
col <> 'x' xと等しくない(!= も多くのDBで使える)
col NOT IN ('x', 'y') xでもyでもない
col NOT BETWEEN 1 AND 5 1〜5の範囲の外
col NOT LIKE 'A%' Aで始まらない
col IS NOT NULL NULLではない
NOT EXISTS (サブクエリ) サブクエリが1行も返さない

NULLが入ると「白」と「白以外」を足しても全件にならない

color が白の行と、白以外の行を数えます。

SELECT COUNT(*) AS c FROM items WHERE color = '白';   -- 4
SELECT COUNT(*) AS c FROM items WHERE color <> '白';  -- 4
SELECT COUNT(*) AS c FROM items WHERE NOT (color = '白');  -- 4

10行あるのに、4件と4件で合計8件です。残りの2件は color が NULL の行で、= でも <> でも NOT でも拾われません。

比較の結果は TRUE・FALSE・NULL の3種類

SQLの条件式は、TRUE(真)・FALSE(偽)のほかに NULL(不明、UNKNOWNとも呼びます) を返します。WHERE句が残すのは TRUE になった行だけです。比較の結果を列として表示すると、様子が分かります。

SELECT id, name, (color = '白') AS is_white, (color <> '白') AS not_white
FROM items WHERE id IN (1, 2, 6);
 id |     name     | is_white | not_white 
----+--------------+----------+-----------
  1 | ノートA5     | t        | f
  2 | ボールペン   | f        | t
  6 | ブックエンド |          | 
(3 rows)

id=6 は color が NULL なので、どちらの比較も NULL(空欄)です。MySQL では同じSQLが 1 / 0 / NULL と表示されます。

AND・OR・NOT と NULL の組み合わせ表

次の表は、PostgreSQL と MySQL で SELECT true AND NULL などを実行して確かめた結果です。

式 結果 覚え方
TRUE AND NULL NULL 相手しだいなので不明
FALSE AND NULL FALSE 片方がFALSEなら全体もFALSE
TRUE OR NULL TRUE 片方がTRUEなら全体もTRUE
FALSE OR NULL NULL 相手しだいなので不明
NOT NULL(値として) NULL 不明の否定は不明

「NOT を付ければ残り全部が取れる」とは限らないのは、NULL を否定しても NULL のままだからです。

NULLの行も含めたいときの書き方

やりたいこと 書き方 件数(今回のデータ)
白以外(NULLは除く) color <> '白' 4件
白以外(NULLも含める) color <> '白' OR color IS NULL 6件
同上(PostgreSQL) color IS DISTINCT FROM '白' 6件
同上(MySQL) NOT (color <=> '白') 6件
NULLの行だけ color IS NULL 2件
color = NULL と書いた場合 比較結果が常にNULL 0件

IS DISTINCT FROM は NULL を「値の1つ」として比べる書き方で、PostgreSQL 16.15 で6件を確認しました。MySQL には NULL も比べられる <=> 演算子があり、こちらも6件でした。

NOT IN とNULLで0件になる落とし穴

NOT IN のリストに NULL が1つでも入ると、結果は必ず0件になります。 値を直接書いた例で確かめます。

SELECT COUNT(*) AS c FROM items WHERE color NOT IN ('赤', '黒');  -- 4
SELECT COUNT(*) AS c FROM items WHERE color NOT IN ('赤', NULL);  -- 0
SELECT COUNT(*) AS c FROM items WHERE color IN ('赤', NULL);      -- 1

PostgreSQL・MySQL とも、上から 4、0、1 でした。NOT IN ('赤', NULL) は白や黒の行があるのに0件です。

なぜ0件になるのか

color NOT IN ('赤', NULL) は、内部では次の形と同じです。

color <> '赤' AND color <> NULL

color <> NULL はどの行でも NULL になります。前の表のとおり TRUE AND NULL は NULL、FALSE AND NULL は FALSE なので、どの行も TRUE にならず、1行も残りません。一方 IN は color = '赤' OR color = NULL なので、赤の行は TRUE OR NULL で TRUE になり、1件返ります。

サブクエリで起きる例と直し方

この現象は、サブクエリの結果に NULL が混ざったときに気づきにくくなります。取り扱いを終えた備品の一覧 discontinued を作り、「一覧に無い備品」を探します。

DROP TABLE IF EXISTS discontinued;
CREATE TABLE discontinued (item_id INT);
INSERT INTO discontinued VALUES (2), (5), (NULL);

直す前(0件)

SELECT COUNT(*) AS c FROM items
WHERE id NOT IN (SELECT item_id FROM discontinued);
 c 
---
 0
(1 row)

直した後(8件)

-- 方法1:サブクエリ側でNULLを除く
SELECT COUNT(*) AS c FROM items
WHERE id NOT IN (SELECT item_id FROM discontinued WHERE item_id IS NOT NULL);

-- 方法2:NOT EXISTS に書き換える
SELECT COUNT(*) AS c FROM items i
WHERE NOT EXISTS (SELECT 1 FROM discontinued d WHERE d.item_id = i.id);

どちらも8件で、PostgreSQL・MySQL の両方で確認しました。

書き方 リストにNULLがあるとき 向いている場面
NOT IN (値のリスト) 0件になる 自分で書いた固定の値
NOT IN (サブクエリ) 0件になる サブクエリの列に NOT NULL 制約があるとき
NOT IN (... WHERE 列 IS NOT NULL) 期待どおり 既存のSQLを小さく直したいとき
NOT EXISTS (...) 期待どおり NULLが入りうる列を相手にするとき

NOT (A OR B) をかっこ無しに書き換える(ド・モルガンの法則)

ド・モルガンの法則は、NOT をかっこの中へ配るときに AND と OR が入れ替わるという規則です。

元の形 書き換えた形
NOT (A OR B) NOT A AND NOT B
NOT (A AND B) NOT A OR NOT B

比較演算子の否定は次のとおりです。

元の条件 否定
= <>
<> =
>= <
> <=
<= >
< >=
IS NULL IS NOT NULL
IN (...) NOT IN (...)
BETWEEN a AND b < a OR > b

書き換えの前後で件数が同じことを確かめる

-- 「文具、または5000円以上」ではないもの
SELECT COUNT(*) AS c FROM items WHERE NOT (category = '文具' OR price >= 5000);  -- 5
SELECT COUNT(*) AS c FROM items WHERE category <> '文具' AND price < 5000;       -- 5

-- 「家電で在庫0」ではないもの
SELECT COUNT(*) AS c FROM items WHERE NOT (category = '家電' AND stock = 0);     -- 9
SELECT COUNT(*) AS c FROM items WHERE category <> '家電' OR stock <> 0;          -- 9

PostgreSQL・MySQL とも 5、5、9、9 でした。NULL を含む color 列でも、書き換えの前後は一致します。

SELECT COUNT(*) AS c FROM items WHERE NOT (color = '白' OR price >= 5000);  -- 3
SELECT COUNT(*) AS c FROM items WHERE color <> '白' AND price < 5000;       -- 3

ド・モルガンの法則は NULL があっても成り立ちます。 NULL の行が外れるのは書き換えのせいではなく、もともとの比較が NULL を返すためです。

PostgreSQL は、この書き換えを自分でも行っています。EXPLAIN の Filter 行を見ると、NOT が消えて AND に変わっています。

EXPLAIN (COSTS OFF)
SELECT * FROM items WHERE NOT (category = '文具' OR price >= 5000);
 Seq Scan on items
   Filter: (((category)::text <> '文具'::text) AND (price < 5000))

どちらの形で書くかは、読みやすさで選んで構いません。 「〜の場合を除く」という仕様なら NOT (...) のままのほうが日本語と対応し、条件が2つ程度なら配った形のほうが短くなります。

IN・BETWEEN・行値比較への書き換え早見表

ANDとORが長く並ぶ条件は、専用の書き方に置き換えると、かっこの付け忘れが起こりにくくなります。件数はすべて今回のデータでの実行結果です。

元の書き方 書き換え 件数
category = '文具' OR category = '家具' OR category = '雑貨' category IN ('文具', '家具', '雑貨') どちらも7件
price >= 500 AND price <= 3000 price BETWEEN 500 AND 3000 どちらも4件
price < 500 OR price > 3000 price NOT BETWEEN 500 AND 3000 どちらも6件
(category = '文具' AND color = '黒') OR (category = '家具' AND color = '白') (category, color) IN (('文具', '黒'), ('家具', '白')) どちらも3件
category > '家電' OR (category = '家電' AND price > 2500) (category, price) > ('家電', 2500) どちらも5件

IN は「ORの連続」を1つにまとめる

IN を使うと、ORが1つの条件にまとまるので、後ろにANDを足してもかっこが要りません。

-- かっこが必要
WHERE (category = '文具' OR category = '家具') AND price >= 1000

-- かっこが不要
WHERE category IN ('文具', '家具') AND price >= 1000

BETWEEN の AND は「条件をつなぐAND」とは別もの

price BETWEEN 500 AND 3000 の AND は BETWEEN の一部で、条件同士をつなぐ AND より先にまとまります。price BETWEEN 500 AND 3000 AND stock > 0 は (price BETWEEN 500 AND 3000) AND (stock > 0) と読まれます。BETWEEN は両端の値を含むので、日時の範囲では終わりの値の扱いに気を配ります。

行値比較(列の組をまとめて比べる)

行値比較は、(列1, 列2) のように複数の列をひと組にして比べる書き方です。

SELECT id, name FROM items
WHERE (category, color) IN (('文具', '黒'), ('家具', '白'))
ORDER BY id;
 id |    name    
----+------------
  2 | ボールペン
  3 | 万年筆
  4 | デスク
(3 rows)

category IN ('文具', '家具') AND color IN ('黒', '白') と書くと「文具の白」も入るので、意味が変わります。「この組み合わせだけ」を指定したいときに行値比較が合います。 大小比較 (category, price) > ('家電', 2500) は、先頭の列から順に比べる形で、PostgreSQL・MySQL とも5件でした。

エラーになる例・意図と違う結果になる例

列名を省いた OR

SELECT * FROM items WHERE price = 300 OR 150;
DB 結果
PostgreSQL 16.15 ERROR: argument of OR must be type boolean, not type integer
MySQL 8.0.46 エラーにならず、10件すべてが返る

MySQL は 0 以外の数値を真として扱うので、OR 150 が常に真になります。文字列でも同じことが起こります。

SELECT COUNT(*) FROM items WHERE category = '文具' OR '家具';
DB 結果
PostgreSQL 16.15 ERROR: invalid input syntax for type boolean: "家具"
MySQL 8.0.46 3件。警告 Truncated incorrect DOUBLE value: '家具'

MySQL では '家具' が数値の 0 に変換されて偽になり、文具の3件だけが返りました。どちらのDBでも price = 300 OR price = 150、または price IN (300, 150) と書けば意図どおりになります。

AND で終わっている・かっこが閉じていない

条件を組み立てる途中で起きやすい2つです。

SELECT * FROM items WHERE price >= 1000 AND;
SELECT * FROM items WHERE (category = '文具' OR category = '家具' AND price >= 1000;
-- PostgreSQL 16.15
ERROR:  syntax error at or near ";"

-- MySQL 8.0.46
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use near '' at line 1

MySQL の near '' は「文の終わりで続きが足りない」ことを表します。開きかっこと閉じかっこの数をそろえると解消します。

直す前と後のまとめ

直す前 起きること 直した後
A OR B AND C Aに C が掛からない (A OR B) AND C
NOT A AND B NOTが A だけに掛かる NOT (A AND B)
price = 300 OR 150 エラー、または全件 price IN (300, 150)
col = NULL 0件 col IS NULL
col <> 'x'(NULLも欲しい) NULLの行が外れる col <> 'x' OR col IS NULL
id NOT IN (NULLを含むサブクエリ) 0件 NOT EXISTS (...)

MySQL・PostgreSQL・Oracle・SQL Serverでの差

NOT > AND > OR の順は4つのDBで共通です。 違いが出るのは、記号の演算子や行値比較などの周辺部分です。

項目 PostgreSQL MySQL Oracle SQL Server
NOT > AND > OR の順 同じ 同じ 同じ 同じ
比較演算子とNOTの関係 比較が先 比較が先 比較が先 比較が先
|| の意味 文字列の連結 既定では OR(非推奨の警告あり) 文字列の連結 使えない(連結は +)
&& を AND として 使えない 使える(非推奨の警告あり) 使えない 使えない
XOR 演算子 なし あり(AND と OR の間の順位) なし なし
! による否定 なし あり(NOT より高い順位) なし なし
条件式を列として表示 できる(t / f) できる(1 / 0) 版による CASE式で包む
行値の IN 実行確認 実行確認 使える 非対応
NULLも比べる演算子 IS DISTINCT FROM <=> DECODE などで代替 IS DISTINCT FROM(2022以降)

PostgreSQL と MySQL の列は今回の実行結果と公式ドキュメントの優先順位表にもとづきます。Oracle と SQL Server は未実行で、優先順位の行は公式ドキュメントの表(Oracle Database 19c の SQL Language Reference、Transact-SQL の Operator Precedence)を開いて確認しました。それ以外の行は一般に知られた仕様として載せています。使う版のドキュメントもあわせて確かめてください。

MySQL で確かめた記号演算子の動き

SELECT 'a' || 'b' AS r;
-- MySQL 8.0.46
+---+
| r |
+---+
| 0 |
+---+
Warning 1287: '|| as a synonym for OR' is deprecated and will be removed in a
future release. Please use OR instead

-- PostgreSQL 16.15
 r  
----
 ab

MySQL では || が OR として働き、文字列が数値に変換されて 0 になりました(SQLモードに PIPES_AS_CONCAT を入れると連結になります)。DBをまたいで使うSQLでは、論理演算は AND・OR・NOT の単語で書くと動きがそろいます。

! と NOT では、まとまる順番が違います。

SELECT NOT 1 = 2 AS not_form, ! 1 = 2 AS bang_form;
+----------+-----------+
| not_form | bang_form |
+----------+-----------+
|        1 |         0 |
+----------+-----------+

NOT 1 = 2 は NOT (1 = 2) で 1(真)、! 1 = 2 は (!1) = 2 と読まれて 0(偽)でした。

SQL Server の優先順位表の読み方

Transact-SQL の公式の表では、OR と同じ段に ALL・ANY・BETWEEN・IN・LIKE・SOME が並んでいます。NOT、AND、OR の順は他のDBと同じなので、ANDとORを混ぜるときにかっこを書く習慣はそのまま通用します。

UPDATE・DELETE の前に条件を確かめる手順

WHERE句の優先順位は、SELECT だけでなく UPDATE や DELETE でも同じです。対象が広がってから気づくことのないよう、次の順で確かめると安心です。

手順 やること
1 同じ WHERE 句で SELECT COUNT(*) を実行し、件数が想定と合うか見る
2 ANDとORが混ざっていたら、ORの側にかっこがあるか見る
3 NOT の後ろが複数条件なら、かっこで囲んであるか見る
4 NULLが入りうる列に <> や NOT IN を使っていないか見る
5 EXPLAIN の Filter 行で、補われたかっこの位置を見る
6 トランザクションの中で実行し、影響行数を確かめてから確定する

よくある質問

Q. ANDとORはどちらが先に評価されますか? A. ANDが先です。A OR B AND C は A OR (B AND C) と読まれます。今回のデータでは、かっこ無しが5件、(A OR B) AND C が3件でした。ORを先にまとめたいときは、ORの側をかっこで囲みます。

Q. 条件を書く順番を入れ替えると、結果は変わりますか? A. ANDだけ、またはORだけでつないだ条件は、順番を入れ替えても結果は同じです。ANDとORが混ざっているときは、順番ではなく優先順位とかっこで結果が決まります。

Q. NOT IN を使ったら0件になりました。なぜですか? A. リストかサブクエリの結果に NULL が含まれている可能性があります。x NOT IN (a, NULL) は x <> a AND x <> NULL と同じで、どの行も TRUE になりません。サブクエリに WHERE 列 IS NOT NULL を足すか、NOT EXISTS に書き換えると解消します。

Q. かっこは多めに付けても問題ありませんか? A. 結果は変わりません。PostgreSQL の EXPLAIN でも、データベース自身がかっこを補って表示していました。ANDとORを混ぜるときは、結果が同じでもかっこを書いておくと意図が伝わりやすくなります。

まとめ

  • 優先順位は「かっこ > NOT > AND > OR」。PostgreSQL・MySQL・Oracle・SQL Server で共通です。
  • ANDとORを混ぜるときはORの側をかっこで囲む。今回のデータでは、かっこ無しが5件、かっこ有りが3件でした。
  • NOTは直後の1条件だけに掛かる。まとめて否定するときは NOT (A AND B) と書きます(2件と9件の差)。
  • 比較の結果には NULL がある。= でも <> でも NULL の行は残らないので、必要なら OR col IS NULL を足します。
  • NOT IN はリストに NULL があると0件。NULLが入りうる列には NOT EXISTS が合います。
  • ド・モルガンの法則で NOT を配ると AND と OR が入れ替わる。書き換えの前後で件数が一致することを確かめました。
  • 迷ったら EXPLAIN の Filter 行。データベースが補ったかっこをそのまま読めます。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL WHEREの複数条件|AND・ORの優先順位とかっこ・NOTの書き方」

株式会社RJCでは、SQLの条件式の書き方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?