メインコンテンツへスキップ
見出し画像

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]のような番号は、押すと出典のページが開きます。

    画像
    この記事で読めるようになる SQL と、扱う内容

    データベースは、情報を「表」で持つ

    表・行・列

    データベースは、情報を表(テーブル)の形で保存します。表計算ソフトのシートに近いもので、横の並びを行、縦の並びを列と呼びます。1行が1件の情報、1列が情報の項目にあたります。

    この記事では、会員の名前・住まい・年齢を並べた、次の表を使います。

    • 表の名前:members(会員)

    • 列:name(名前)・city(住まい)・age(年齢)

    • 行:tanaka(久留米・34歳)、suzuki(福岡・28歳)、sato(久留米・45歳)の3人分

    画像
    members の表。横の1行が1人分の情報、縦の1列が項目にあたる

    SQL は、データベースへの命令文

    SQL は、データベースに「この表から、この条件の行を取り出して」などと頼むための言葉です。英単語に似た決まった言葉(キーワード)と、表や列の名前、値を並べて、1つの文を作ります。

    キーワードは、大文字で書いても小文字で書いても同じ意味になります。PostgreSQL の文書も、キーワードは大文字と小文字を区別しないとしています[4]。SQLite でも、select name, age from members と小文字で書いて、大文字の場合と同じ結果が返りました。この記事では、読みやすさのために、キーワードを大文字で書きます。

    SELECT と FROM:どの表から、どの列を取り出すか

    行を取り出す SQL は、SELECT という言葉で始まります。いちばん短い形は、次のとおりです。

    SELECT name FROM members
    • SELECT name:name の列を取り出す

    • FROM members:members の表から

    実行すると、name の列だけが、3人分の3行で返りました。複数の列がほしいときは SELECT name, age のように「,」で区切り、すべての列がほしいときは列の名前の代わりに「*」と書きます[2]。SELECT * FROM members では、name・city・age の3つの列が返りました。

    画像
    SELECT の後ろに取り出す列、FROM の後ろに表の名前を書く。返ってくる結果も、行と列の表の形になる

    WHERE:条件に合う行だけを選ぶ

    表の中から特定の行だけがほしいときは、後ろに WHERE と条件を付けます。

    SELECT name FROM members WHERE city = '久留米'

    データベースは、WHERE の条件を表の1行ずつに当てはめ、条件が成り立った行だけを結果に入れます[2]。この SQL では、住まいが久留米の tanaka と sato の2行が返りました。

    表にない住まい(東京)を条件にすると、どの行も条件に合わず、結果は0行でした。0行は誤りではなく、「該当する行がない」という結果です。

    画像
    WHERE の条件を1行ずつ当てはめ、成り立った行だけが返る。どの行も合わなければ、結果は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)で表示されました。表し方は違いますが、意味は同じです。

    画像
    条件を行ごとに当てはめると、真か偽になる。SQLite は1と0、PostgreSQL は t と f で表すが、意味は同じ

    比べる記号

    条件には、「=」のほかに、大小を比べる記号も使えます。PostgreSQL の文書に並ぶ主なものは、次のとおりです[6]。

    • =:等しい

    • <>:等しくない(!= と書くこともできる)

    • >・<:より大きい・より小さい

    • >=・<=:以上・以下

    members の表で試すと、age > 30 では tanaka と sato、age < 30 では suzuki、age <> 34 では suzuki と sato が返りました。どの記号も、行ごとに真か偽を決め、真の行だけが返るという点は同じです。

    画像
    比べる記号の意味と、members の表で真になった行

    大文字と小文字は、区別されることがある

    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」という誤りになった

    画像
    「'」で囲んだ部分だけが文字の値になる。数は囲まない。囲まないと列の名前として読まれ、閉じ忘れると文として読めなくなる(誤りの文言は SQLite のもの)

    値の中に「'」を入れたいとき

    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 は並列につないだもの(どちらか一方で通る)にあたります。

    画像
    AND は両方が真のときだけ真(スイッチの直列)。OR はどちらか一方でも真なら真(スイッチの並列)

    表で試す

    members の表で、実際に組み合わせてみます。

    • city = '久留米' AND age >= 40:久留米に住んでいて、かつ40歳以上。両方が真になるのは sato だけで、1行が返った

    • city = '福岡' OR age >= 40:福岡に住んでいるか、または40歳以上。suzuki(福岡)と sato(45歳)の2行が返った

    tanaka は、久留米には住んでいますが34歳なので、AND の条件では「真 かつ 偽」で偽になり、OR の条件でも「偽 または 偽」で偽になります。

    画像
    行ごとに2つの条件の真・偽を確かめ、AND と OR でまとめる。AND では sato だけ、OR では suzuki と sato が返る

    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 かで、結果が変わることがあります。

    画像
    AND は OR より先に結びつく。かっこを付けると、先にまとめる部分と、最後にまとめる言葉が入れ替わる

    順番を取り違えると、思わぬ行が返る

    たとえば「福岡か久留米に住んでいて、40歳以上の会員」を探すつもりで、次のように書いたとします。

    SELECT name FROM members WHERE city = '福岡' OR city = '久留米' AND age >= 40

    40歳以上は sato だけなので、結果は sato の1行になるはずです。ところが実際には、suzuki と sato の2行が返りました。

    suzuki の行で、順を追って確かめます。この行では、3つの条件はそれぞれ次のようになります。

    • city = '福岡':真

    • city = '久留米':偽

    • age >= 40:偽(28歳)

    かっこがないので、AND の両側を先にまとめます。

    1. 「久留米に住んでいる」かつ「40歳以上」:偽 かつ 偽 → 偽

    2. 「福岡に住んでいる」または「1の結果」:真 または 偽 → 真

    最後が OR なので、「福岡に住んでいる」が真というだけで、行全体が真になり、suzuki の行が返ります。この条件は、「福岡に住んでいる」または「久留米に住んでいて、かつ40歳以上」という意味に読まれていたことになります。tanaka と sato の行も含めて、3人の行ごとに追うと次のようになります。

    画像
    かっこがない場合を、3人の行ごとに2段で追う。①で「久留米 かつ 40以上」を先にまとめ、②で「福岡」と OR でまとめるため、福岡の suzuki の行も返る

    意図どおりにするには、かっこで OR を先にまとめます。

    SELECT name FROM members WHERE (city = '福岡' OR city = '久留米') AND age >= 40

    同じ suzuki の行で確かめると、次のようになります。

    1. 「福岡に住んでいる」または「久留米に住んでいる」:真 または 偽 → 真

    2. 「1の結果」かつ「40歳以上」:真 かつ 偽 → 偽

    最後が AND なので、年齢の条件が偽というだけで、行全体が偽になります。結果は sato の1行だけで、意図どおりになりました。PostgreSQL でも、同じ2つの SQL で同じ結果になりました。3人の行ごとに追うと次のようになり、最後の AND で「40以上」が偽の行はすべて外れます。

    画像
    かっこがある場合を、同じく行ごとに追う。①で「福岡 または 久留米」をまとめ、②で「40以上」と AND でまとめるため、40歳以上の sato の行だけが返る

    いつも真になる条件

    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 の意味を変えてしまう攻撃)の仕組みを読むときの要になります。

    画像
    いつも真になる条件は、AND でつなぐと結果を変えないが、OR でつなぐと前半の条件に関係なく全員の行を返す

    NULL:値がないとき

    真でも偽でもない「不明」

    表には、値が入っていない欄ができることがあります。たとえば、新しい会員 ito(福岡)が年齢を登録していない場合です。SQL では、このような「値がない」状態を NULL(ヌル)と呼びます。NULL は、0とは別のもので、値が「分からない」ことを表します[6]。

    NULL を条件に当てはめると、結果は真でも偽でもなく、不明(NULL)になります[6]。ito の年齢は分からないので、「年齢が30以上か」は、真とも偽とも言えないからです。

    • SELECT name, age >= 30 FROM members では、tanaka と sato が真、suzuki が偽、ito は NULL(不明)になった

    画像
    年齢が NULL の ito の行では、「30以上か」が真でも偽でもなく、不明(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人が返りました。

    画像
    WHERE は真の行だけを返す。不明の行は「30以上」でも「30未満」でも返らない。NULL を探すときは = ではなく IS NULL を使う

    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

    画像
    この記事のまとめ。表と SELECT、真と偽、AND・OR の順番、NULL

    SQL は、決まった言葉と記号の並びで意味が決まる言葉です。条件が行ごとに真・偽・不明のどれになるか、AND と OR がどこから結びつくかを押さえておくと、短い SQL の意味を、一語ずつ読み解けるようになります。

    免責事項

    この記事は、筆者が個人として調べたことや経験をもとに、執筆時点の内容をまとめたものです。分かりやすさを優先し、細かい条件や例外を省いている場合があります。

    動作は Python 3.13・SQLite 3.53・PostgreSQL 16 で確かめたもので、データベースの種類やバージョン、設定によって変わることがあります。実際の開発では、公式の文書で最新の内容を確認してください。

    製品の仕様などは変わることがあります。この記事の内容を利用して生じた損害については、責任を負いかねます。

    NDシステムズについて

    NDシステムズは、福岡県久留米市を拠点に、ホームページの制作、業務に合わせた事務のシステムの開発、AI・DX支援を行っています。まずはお気軽に無料相談でご相談ください。

    参考文献

    1. SQL Language Expressions(SQLite)(英語)

    2. The SELECT statement(SQLite)(英語)

    3. SQLite Keywords(SQLite)(英語)

    4. 4.1. Lexical Structure(PostgreSQL 16 Documentation)(英語)

    5. 9.1. Logical Operators(PostgreSQL 16 Documentation)(英語)

    6. 9.2. Comparison Functions and Operators(PostgreSQL 16 Documentation)(英語)

    7. Case Sensitivity in String Searches(MySQL 8.4 Reference Manual)(英語)

     
     
    福岡県久留米市のNDシステムズです。IT・AI、語学、会計、数学、九州北部の風景などについて、調べて考えたことを書いています。記事は筆者が個人として調べた執筆時点の内容で、専門家の助言ではありません。最新の情報は出典でご確認ください。

    あなたへのおすすめ