
SQLの基本の読み方。SELECT・WHERE、真と偽、AND・OR、NULLまで
ウェブサイトの検索や会員の管理の裏側では、プログラムがデータベースにSQLという命令文を送り、必要な情報を取り出しています。この記事では、SQL になじみのない方に向けて、SQL の基本の読み方を、小さな会員の表を例に説明します。
扱うのは、表・行・列、SELECT・FROM・WHERE、条件が真(true)か偽(false)になるという考え方、比べる記号、文字の値を「'」で囲む決まり、AND・OR・NOT とその結びつきの順番、そして値がない状態を表す NULL です。
例はすべて、Python 3.13 に付属の SQLite 3.53 で実際に動かし、主なものは PostgreSQL 16 でも確かめました。本文の[1]のような番号は、押すと出典のページが開きます。

データベースは、情報を「表」で持つ
表・行・列
データベースは、情報を表(テーブル)の形で保存します。表計算ソフトのシートに近いもので、横の並びを行、縦の並びを列と呼びます。1行が1件の情報、1列が情報の項目にあたります。
この記事では、会員の名前・住まい・年齢を並べた、次の表を使います。
表の名前:members(会員)
列:name(名前)・city(住まい)・age(年齢)
行:tanaka(久留米・34歳)、suzuki(福岡・28歳)、sato(久留米・45歳)の3人分

SQL は、データベースへの命令文
SQL は、データベースに「この表から、この条件の行を取り出して」などと頼むための言葉です。英単語に似た決まった言葉(キーワード)と、表や列の名前、値を並べて、1つの文を作ります。
キーワードは、大文字で書いても小文字で書いても同じ意味になります。PostgreSQL の文書も、キーワードは大文字と小文字を区別しないとしています[4]。SQLite でも、select name, age from members と小文字で書いて、大文字の場合と同じ結果が返りました。この記事では、読みやすさのために、キーワードを大文字で書きます。
SELECT と FROM:どの表から、どの列を取り出すか
行を取り出す SQL は、SELECT という言葉で始まります。いちばん短い形は、次のとおりです。
SELECT name FROM membersSELECT name:name の列を取り出す
FROM members:members の表から
実行すると、name の列だけが、3人分の3行で返りました。複数の列がほしいときは SELECT name, age のように「,」で区切り、すべての列がほしいときは列の名前の代わりに「*」と書きます[2]。SELECT * FROM members では、name・city・age の3つの列が返りました。

WHERE:条件に合う行だけを選ぶ
表の中から特定の行だけがほしいときは、後ろに WHERE と条件を付けます。
SELECT name FROM members WHERE city = '久留米'データベースは、WHERE の条件を表の1行ずつに当てはめ、条件が成り立った行だけを結果に入れます[2]。この SQL では、住まいが久留米の tanaka と sato の2行が返りました。
表にない住まい(東京)を条件にすると、どの行も条件に合わず、結果は0行でした。0行は誤りではなく、「該当する行がない」という結果です。

条件は「真」か「偽」になる
真(true)と偽(false)
WHERE の後ろの city = '久留米' は、「city の列の値が、久留米と等しいか」を確かめる条件です。条件を1つの行に当てはめた結果は、成り立つ(真、英語で true)か、成り立たない(偽、英語で false)かのどちらかになります(値がない場合の例外は、後の NULL の節で扱います)。
WHERE は、条件が真になった行だけを返し、偽になった行は外します[2]。SQL を読むときは、「この条件は、この行で真になるか、偽になるか」を1行ずつ考えるのが基本です。
結果の表し方は、データベースによって違う
条件の結果は、SELECT の後ろに書いて、直接見ることもできます。SELECT name, age, age >= 30 FROM members と書くと、行ごとに「年齢が30以上か」の結果が並びます。
tanaka(34歳):真
suzuki(28歳):偽
sato(45歳):真
このとき SQLite は、真を 1、偽を 0 と表示しました。この1と0は、足したりかけたりする数ではなく、真と偽を表す印です。SQLite の文書も、TRUE と FALSE は、たいていの場合、1と0の別名にすぎないとしています[1]。同じ SQL を PostgreSQL で実行すると、t(true)と f(false)で表示されました。表し方は違いますが、意味は同じです。

比べる記号
条件には、「=」のほかに、大小を比べる記号も使えます。PostgreSQL の文書に並ぶ主なものは、次のとおりです[6]。
=:等しい
<>:等しくない(!= と書くこともできる)
>・<:より大きい・より小さい
>=・<=:以上・以下
members の表で試すと、age > 30 では tanaka と sato、age < 30 では suzuki、age <> 34 では suzuki と sato が返りました。どの記号も、行ごとに真か偽を決め、真の行だけが返るという点は同じです。

大文字と小文字は、区別されることがある
SQLite と PostgreSQL の初期設定では、'Tanaka' = 'tanaka' は偽になりました。文字の比較では、大文字と小文字が別の文字として扱われたことになります。
一方、MySQL の初期設定では、文字の比較で大文字と小文字を区別しません[7]。どちらになるかはデータベースの種類や設定で変わるため、使うデータベースで確かめる必要があります。
文字の値は「'」で囲む
「'」は、値の始まりと終わりの目印
city = '久留米' の '久留米' のように、SQL では、文字の値を「'」(シングルクォート)で囲みます[1]。最初の「'」が値の始まり、次の「'」が値の終わりの目印で、その間にあるものだけが、文字の値として扱われます。
一方、age = 34 のように、数の値は「'」で囲みません。
「'」を付け忘れると、意味が変わります。
name = tanaka(囲まない):tanaka は文字の値ではなく、列の名前として読まれる。tanaka という列はないため、SQLite では「no such column: tanaka」、PostgreSQL では「column "tanaka" does not exist」という誤りになった
name = 'tanaka(閉じ忘れ):値の終わりが見つからず、SQLite では「unrecognized token」という誤りになった

値の中に「'」を入れたいとき
O'Hara のように、値そのものに「'」を含めたいときは、「'」を2つ続けて書きます[1]。'O''Hara' と書くと、データベースは O'Hara という文字の値として受け取りました。PostgreSQL の文書にも、同じ書き方が示されています[4]。
なお、「"」(ダブルクォート)は「'」と役割が異なります。SQLite と PostgreSQL の文書では、「"」で囲んだものは値ではなく、表や列の名前として扱われます[3][4]。ただし SQLite は、古い SQL との互換のために、この決まりを例外的に緩める場合があるとしています[3]。
AND・OR・NOT:条件を組み合わせる
AND は「かつ」、OR は「または」
条件は、AND や OR でつないで、1つにまとめることができます。
AND(かつ):前と後ろの条件が両方とも真のときだけ、真
OR(または):前と後ろの条件のどちらか一方でも真なら、真
4通りの組み合わせを SQLite で調べると、次のとおりでした。PostgreSQL の文書の真理値表(組み合わせごとの結果の表)とも一致します[5]。
真 と 真:AND は真、OR は真
真 と 偽:AND は偽、OR は真
偽 と 真:AND は偽、OR は真
偽 と 偽:AND は偽、OR は偽
AND が真になるのは、両方が真のときの1通りだけです。OR が偽になるのは、両方が偽のときの1通りだけです。
電気の回路のスイッチにたとえると、AND は2つのスイッチを直列につないだもの(両方入れないと電気が通らない)、OR は並列につないだもの(どちらか一方で通る)にあたります。

表で試す
members の表で、実際に組み合わせてみます。
city = '久留米' AND age >= 40:久留米に住んでいて、かつ40歳以上。両方が真になるのは sato だけで、1行が返った
city = '福岡' OR age >= 40:福岡に住んでいるか、または40歳以上。suzuki(福岡)と sato(45歳)の2行が返った
tanaka は、久留米には住んでいますが34歳なので、AND の条件では「真 かつ 偽」で偽になり、OR の条件でも「偽 または 偽」で偽になります。

NOT は、真と偽を入れ替える
NOT は、後ろの条件の真と偽を入れ替えます[5]。NOT city = '久留米' は「久留米に住んでいない」という意味になり、suzuki の1行が返りました。
AND は OR より先に結びつく
かけ算が足し算より先なのと同じ
1つの条件の中に AND と OR が両方あるときは、どちらで先に条件をまとめるかが問題になります。SQL では、AND が OR より先に結びつきます[1]。PostgreSQL の文書の演算子の表でも、AND は OR より上の順位に置かれています[4]。
算数の「1+2×3」で、かけ算を足し算より先に計算するのと同じ関係です。順番を変えたいときは、算数と同じく、かっこを付けます。
かっこがない A OR B AND C:先に「B かつ C」をまとめ、最後に A と OR でまとめる
かっこがある (A OR B) AND C:先に「A または B」をまとめ、最後に C と AND でまとめる
並びは同じでも、最後にまとめる言葉が OR か AND かで、結果が変わることがあります。

順番を取り違えると、思わぬ行が返る
たとえば「福岡か久留米に住んでいて、40歳以上の会員」を探すつもりで、次のように書いたとします。
SELECT name FROM members WHERE city = '福岡' OR city = '久留米' AND age >= 4040歳以上は sato だけなので、結果は sato の1行になるはずです。ところが実際には、suzuki と sato の2行が返りました。
suzuki の行で、順を追って確かめます。この行では、3つの条件はそれぞれ次のようになります。
city = '福岡':真
city = '久留米':偽
age >= 40:偽(28歳)
かっこがないので、AND の両側を先にまとめます。
「久留米に住んでいる」かつ「40歳以上」:偽 かつ 偽 → 偽
「福岡に住んでいる」または「1の結果」:真 または 偽 → 真
最後が OR なので、「福岡に住んでいる」が真というだけで、行全体が真になり、suzuki の行が返ります。この条件は、「福岡に住んでいる」または「久留米に住んでいて、かつ40歳以上」という意味に読まれていたことになります。tanaka と sato の行も含めて、3人の行ごとに追うと次のようになります。

意図どおりにするには、かっこで OR を先にまとめます。
SELECT name FROM members WHERE (city = '福岡' OR city = '久留米') AND age >= 40同じ suzuki の行で確かめると、次のようになります。
「福岡に住んでいる」または「久留米に住んでいる」:真 または 偽 → 真
「1の結果」かつ「40歳以上」:真 かつ 偽 → 偽
最後が AND なので、年齢の条件が偽というだけで、行全体が偽になります。結果は sato の1行だけで、意図どおりになりました。PostgreSQL でも、同じ2つの SQL で同じ結果になりました。3人の行ごとに追うと次のようになり、最後の AND で「40以上」が偽の行はすべて外れます。

いつも真になる条件
1 = 1 のように、同じものどうしを比べる条件は、どの行に当てはめてもいつも真になります。表の中身を見ずに、結果が決まってしまう条件です。
このような条件を AND と OR でつなぐと、次のようになりました。
WHERE 1 = 1:どの行でも真なので、3人全員の行が返る
WHERE age >= 30 AND 1 = 1:AND の後ろがいつも真なので、結果は前半の条件で決まり、tanaka と sato の2行(1 = 1 がない場合と同じ)
WHERE city = '東京' OR 1 = 1:OR の後ろがいつも真なので、前半に関係なく、3人全員の行が返る
AND でつないだときは結果を変えませんが、OR でつなぐと、前半の条件がどうであれ、すべての行が返ります。この性質は、別の記事で扱う SQLインジェクション(入力した文字で SQL の意味を変えてしまう攻撃)の仕組みを読むときの要になります。

NULL:値がないとき
真でも偽でもない「不明」
表には、値が入っていない欄ができることがあります。たとえば、新しい会員 ito(福岡)が年齢を登録していない場合です。SQL では、このような「値がない」状態を NULL(ヌル)と呼びます。NULL は、0とは別のもので、値が「分からない」ことを表します[6]。
NULL を条件に当てはめると、結果は真でも偽でもなく、不明(NULL)になります[6]。ito の年齢は分からないので、「年齢が30以上か」は、真とも偽とも言えないからです。
SELECT name, age >= 30 FROM members では、tanaka と sato が真、suzuki が偽、ito は NULL(不明)になった

WHERE は、不明の行も返さない
WHERE は、条件が真の行だけを返し、偽の行も、不明の行も外します[2]。そのため、ito の行は、次のどちらの SQL でも返りませんでした。
WHERE age >= 30:tanaka と sato
WHERE age < 30:suzuki だけ
「30以上」と「30未満」を合わせても、全員にはなりません。NOT age >= 30 と書いても、不明を入れ替えた結果はやはり不明なので、返るのは suzuki だけでした。
NULL を探すときは IS NULL
年齢が登録されていない会員を探そうとして、WHERE age = NULL と書くと、結果は0行でした。NULL と比べた結果は、相手の値に関係なく不明になり(NULL どうしでも、等しいとは言えません)、この条件はどの行でも真にならないからです。PostgreSQL の文書も、= NULL とは書かないよう注意しています[6]。
NULL かどうかを確かめるには、IS NULL を使います[6]。WHERE age IS NULL では ito の1行が、WHERE age IS NOT NULL では残りの3人が返りました。

AND・OR と不明
不明が AND や OR でまとめられたときは、不明でない方の条件だけで答えが決まれば、その答えになります。決まらなければ、結果も不明です[5]。SQLite と PostgreSQL で試すと、次のとおりでした。
偽 かつ 不明:偽(片方が偽なら、もう一方に関係なく偽)
真 または 不明:真(片方が真なら、もう一方に関係なく真)
真 かつ 不明:不明(もう一方しだいで、どちらにもなりうる)
偽 または 不明:不明(同じく、もう一方しだい)
たとえば city = '福岡' OR age >= 40 では、ito の行は「真 または 不明」で真になり、suzuki・sato と一緒に返りました。
まとめ
データベースは情報を表で持つ。横の1行が1件、縦の1列が項目
SELECT 列 FROM 表 で、表から列を取り出す。WHERE 条件 を付けると、条件を1行ずつ当てはめ、真になった行だけが返る
条件の結果は、真(true)か偽(false)。SQLite は1と0、PostgreSQL は t と f で表すが、意味は同じ(値がなければ不明)
文字の値は「'」で囲み、数は囲まない。値の中の「'」は2つ続けて書く
AND は両方が真のとき、OR はどちらかが真のときに真。NOT は真と偽を入れ替える
AND は OR より先に結びつく。意図と違う順番になるときは、かっこでまとめる
NULL は値がないこと。条件は不明になり、WHERE では返らない。NULL を探すときは IS NULL

SQL は、決まった言葉と記号の並びで意味が決まる言葉です。条件が行ごとに真・偽・不明のどれになるか、AND と OR がどこから結びつくかを押さえておくと、短い SQL の意味を、一語ずつ読み解けるようになります。
免責事項
この記事は、筆者が個人として調べたことや経験をもとに、執筆時点の内容をまとめたものです。分かりやすさを優先し、細かい条件や例外を省いている場合があります。
動作は Python 3.13・SQLite 3.53・PostgreSQL 16 で確かめたもので、データベースの種類やバージョン、設定によって変わることがあります。実際の開発では、公式の文書で最新の内容を確認してください。
製品の仕様などは変わることがあります。この記事の内容を利用して生じた損害については、責任を負いかねます。
NDシステムズについて
NDシステムズは、福岡県久留米市を拠点に、ホームページの制作、業務に合わせた事務のシステムの開発、AI・DX支援を行っています。まずはお気軽に無料相談でご相談ください。