昨今ではフレームワークのORMなどに依存しすぎて、エンジニアのSQLに対する知識が希薄になっているという傾向を耳にすることがあります。その理由は、SQLの構造がよくわかっていないというもので、これはどうもSQLを独自の言語だと錯覚しているようにも感じます。
実はあまり知られていない事柄なのですが、SQLは基本的に、英文法ルールに即して、命令文を組み立てられるようになっています。SQLもStructured Query Languageを略したものであり、「組み立てられた問い合わせ言語」という意味です。したがって、そこを意識しておいた方が書き方を覚えるのも楽です(ただ、自動詞、他動詞、前置詞のルールにとどまらず、関係詞やら準動詞やら思いっきり高校レベルの英文法知識も必須です)。
そしてこれを理解することで、クエリに問い合わせするSELECT文がなぜSELECT句、FROM句、WHERE句、GROUP BY句、HAVING句、そしてORDER BY句の順番なのか、なぜINSERTがINTOでUPDATEがSETなのか、その他結合に用いるJOINの仕組みなどもくっきりと理解できるようになります。
ちなみに、queryとは文語表現で「問い合わせる」という意味ですが、後述する「副問合せ」という日本語表現もそこから来ています。また、昨今の英和辞典では専門用語として「DBから検索する」動詞としても紹介されています。
select句とfrom句
select句とfrom句は基礎の基礎であり、select文を使用する際には、最低この二句が必須となります。では、まずselect句は取得カラムを記述し、from句(使用するテーブルを選択する句)の後には参照元となるテーブルを記述します。
たとえば、書籍マスタから書籍の名前を取得する場合、以下のような記述となります。
#select name from MstBooks.
ではこれを英文法的に解釈していきます。selectは選択するという他動詞で、他動詞の場合は必ず目的語が必要となります。なのでnameとカラム名を指定しています。これが文頭に来ているのだから「選んでこい」という命令文となります。
対してfromは、自分から見て、外部の範疇から手元に取り出すことを意味する前置詞で、前置詞の後は、ほぼ必ず名詞が来ます。つまり、from MstBooksというのは書籍マスタからという、元の場所を意味します。
それを踏まえ
select name from MstBooks
は、「MstBooksという書籍マスタからnameというカラムを選んでこい」とデータベースに命令(クエリ問い合わせ)を送っているのです。
ここまでは、ほんの基本ですが、これ以降はかなり高度な英文法の説明となります。なお、その解釈にあたっては、海外ドキュメントのSQL解説記事及び、英文法ルールに際して旺文社の『ロイヤル英文法』や時吉秀弥氏の著書などを参考にしています。
where句
where句は条件指定のための句です。つまりデータをどのような条件で持ってくるかを指定します。
例:書籍トランザクションから小学館の書籍を取得する
# select * from MstBooks where publisher = '小学館';
なぜ条件文はwhereなのか?
なぜ、検索条件を付与する句がwhereなのか疑問に思った人もいるかも知れません。実はこのwhereは場所を示す関係副詞に基づきます。上の例だと、publisher(出版社)が小学館であるMstBooksへ命令を送ることになります。
ここで関係詞の軽いおさらいですが、基本は補足説明を一文にするための、主に文章表現で用いられる用法です。今回は、以下の2つの命令が意味として込められています。
Select the book from MstBooks. MstBooksからある本を選べ
The publisher is Shogakkan in MstBooks. その出版社は(MstBooks内の)小学館だ
SQLはこれを一文にしないといけないので、ひとまず接続詞のthatを使って一文にしてみます。すると…
Select books from MstBooks that the publisher is Shogakkan in MstBooks.
となります。これだとin MstBooksが冗長なので、MstBooksを省略してみます。その場合は、関係詞whichで代用することで省略可能です。ただ、その場所を示す前置詞inは最後に残さねばなりません(ちなみに関係詞thatは前置詞が残るので、この場合は使用できません。また厳密に言うと関係詞のwhichとthatは使い分けされ、特定多数の絞り込み条件の場合はwhichを使うのが基本ルールです)。
Select books from MstBooks which the publisher is Shogakkan in.
これだとwhich以下の文がinで終わっている(※文法的に間違いではないです)ので、後に紹介するin条件と紛らわしくなります。なので、whichを使用していた場合、前置詞の場所は以下のように関係詞whichの目前に移動できます。
Select books from MstBooks in which the publisher is Shogakkan.
そして、このような並びになった場合、場所を示すin whichは関係副詞whereに置き換えることができます。
Select books from MstBooks where the publisher is Shogakkan.
これで、だいぶSQLの命令文に近づいてきたと思います。あとは定冠詞theを外し、isを=に置き換えるだけです。ただSQLではisと=は意味が違う(isを使うのはNULL判定の時ぐらい)ので、最終形が上記のSQL文になります。
=とisの違い。なぜnullはisなのか?
これを説明するには、まずbe動詞のisについて役割を知っておく必要があります。よく A is Bという文はSVC(主語+動詞+補語)の文法に即して、AがBであるという同格と説明されることが多いですが、厳密にはAがBの状態にあるという現在の状況を意味しています。これをSQL文に当てはめ、もしも検索条件がpublisher is NULL
の場合、publisherはNULLという状態にあることを示しています。
対してA = Bというのは、ANSIによって規格化されており BをAに代入しても等位の関係を保つことができる、すなわちSQLにおいては、関係は常に真である ということを意味しています。したがってpublisher = '小学館'
は、publisherが小学館という条件は常に真である ということです。
ではpublisher = NULL だと何がまずいのかということですが、NULLというのはそもそもの値が存在していないという状態なので、比較対象が異なりNULLであるか、そうでないかの判断しかしません。つまり、値を代入する場合と判断基準が異なっているのです。
また、これはなぜ副問合せに=
を使用するとエラーが発生することがあるのか、という部分に大きく関わってくる要素です。
like構文
like句は部分一致のための予約語で、前方一致はlike 'hoge%'、中間一致はlike '%hoge%'、そして後方一致は like '%hoge' となります。ここは多分大丈夫だと思いますが、likeは「~のような」という近似を示す副詞です(好きのlikeは語源が違います)。
数値を指定する
SQLでは数値に対してwhere句で >(超)、>=(以上)、<(未満),<=(以下)といった不等号指定と、あとbetweenという範囲指定ができます。
範囲指定
範囲指定はbetweenを用い、必ず●●以上●●以下の指定となります。betweenは(※基本)2つの、確定している対象間を意味する前置詞です。
※英文法では、ある条件(*)に限り3つ以上でも使う場合があります。ただ、そこを追及すると却って混乱を招いてしまうので今回は註釈にとどめておきますが、とりわけSQLの場合は、2つの値の間と覚えてしまっていいでしょう。
※確定した対象かつ同種の対比の場合は使用可能。対するamongは数が不確定な対象。
in句
inは条件指定の句であり、SQLでは同じカラムに対しor条件を付与したり、副問合せ(別のテーブルから該当する値を問い合わせること)に用いたりします。
※このin句が便利なのは、対象が同じならば複数のor条件に対しても簡単に記述できることです。また、このinは後述する副問合せでも頻用します。
例:ジャンルが文庫または新書の書籍を取得する。
#select name from MstBooks where genre in ('文庫','新書')
なぜinでor条件を示すのか?
次に述べる副問合せでも大事な説明となります。なぜinを用いれば、or条件を付与できるのでしょうか。
inは(広さを問わない)一定領域内部への存在意識を意味する前置詞です。上記の例でいえば、in以下にある[…]オブジェクトの内部に存在する、文庫または新書を指示しています。つまり、書籍がいっぱいある空間(図書館をイメージするといいです)があり、そこには文庫や新書や文芸や辞書などジャンルは数あれど、その中の文庫か新書ならどっちでもいいという、そんなニュアンスを持ちます。
なので、inが示すのは、andという絞り込み条件ではない のは一目瞭然だと思います。
副問合せをする
副問合せはサブクエリとも言います。この副問合せは非常によく使用します。
副問合せもこのinなどを用います。たとえば出版社マスタにrun_flg(経営状況)というカラムがあり、それがTRUEの書籍、つまり現存出版社のみを検索条件とし、書籍マスタにおいては複数の書籍が条件に合致するはずです。
例:現存出版社を条件として、書籍マスタから書籍名を取得する
#select name from MstBooks where publisher in (select name from MstPublisher where run_flg = RUE)
条件式にinを用いると、該当するデータを一式取得できるのは、前述した通りinは内部感覚、すなわちor条件を示すからで、上記だと経営中の出版社という条件に該当する全書籍名を取得することになります。
したがって、副問合せのin条件を=にしてしまうとまずいことになる理由もこれで解ります。複数ヒットした時点でSQLはエラーを返しますが、これは先ほどの=の説明にあった通り、式の真偽関係が成立しなくなるからです。
exists
副問合せはexistsという条件絞り込みも利用できます。その場合は、条件を絞り込んでいきたい(引き算にする)場合、すなわち否定条件を付与して、消去法で用いることが多いです。
例:現存しない出版社の書籍を取得する
#select name from MstBooks where not exists(select 1 from MstPublisher where run_flg = TRUE)
exists構文にカラム名が付かない理由
※このexistsはなぜinと違い、検索条件のカラムを付与しないのか、なぜまたexistsと三単現の「s」が付くのかと疑問に思った人は多いと思います。まず、このexistは「存在する」という意味ですが、自動詞にしか使えないので目的語を取れません。したがって先程のwhereの説明にあった通り、関係副詞whereには前置詞inが隠れており、本当は
MstBooksの値 exists in 副問合せ文.
のような文章になっています。これを日本語に解釈すると「MstBooksの値は 副問合せ文(現存出版社の情報)に存在している」ことを意味しています。
またexistsと三単現(三人称単数現在形)になっているのもちゃんと意味があり、主となるMstPublisherテーブルに対し、参照にしているのはMstBooksテーブル内の値です。つまり英語的な感覚では(出版社マスタにとっては)、書籍マスタは第三者による値という客観的な解釈をしているからです。
そしてnotが付与されているので、その条件の否定、すなわち「~でない」という条件となり、経営状況がTRUEでない、つまりは消滅企業を意味します。
group by句
group by句はカラムの条件を一つないしは複数のグループ化し、それに対し合計値や平均値、件数を取得したりするものです。その場合、where条件には計算関数以外のカラムを併記する必要があります。
例1:出版社ごとの辞書の冊数を取得する
#select count(name) as cnt,name from MstBooks where genre = '辞書' group by name;
例2:出版社ごとの辞書の売上実績を取得する。
#select count(name) as cnt,name from MstBooks where genre = '辞書' group by name;
このgroup byのgroupは何を示しているのか?
groupとは「分かれる」という自動詞 (オーレックス英和より)のようです。そしてby(原義は「傍に」という意味で、関わり合いのあるものを意味する前置詞)はこの場合、目的達成のための手段を意味し、ここでは「分かれた」という意味合いとなります。
例文:操業中の出版社ごとの冊数を取得する
#select count(*), publisher from MstBooks where run_flg = TRUE group by publisher.
つまり、書籍マスタは出版社名で分別されるという意味合いになります。また、where句が挟まっていてもその意味に変更はありません。なぜなら、前述した通りにwhere句はあくまでMstBooksを修飾する関係副詞なので、group byに直接係らないからです。
ただ、この部分についてgroup byの他にもorder by、limitなども関係しているのですが、まだ解釈途中で、自分が納得していない部分も多いです(もし関係詞だとすると、主格なのに省略していることになる)。色々情報を調べていると有力な見方として、間にはandが挟まっているのではないか、つまりselect句とは別にgroup、order…と命令文を分けているのではという考察がありました。そうだとするとhaving句がgroup by句にしか係っていないことの説明もつくので、一旦はそのように話を進めていきます。
having句
having句はグループ化されたものに対し、条件を付与する場合です。そして、このhaving句も、どういう条件で使用する句なのか、けっこう混乱を招いたことがあるのではないでしょうか。
例:書籍トランザクションで10冊以上の辞書を持つ出版社を取得する
#select count(name) as cnt,name from TrnBooks where genre = '辞書' group by name having cnt >= 10;
このようにグループ化されたカラムに対し、条件を付与する場合に用います。では、なぜグループ化の条件なのか、それも英文法から解釈すれば明白です。
なぜhavingと~ing表記なのか?
※havingは、なぜこれだけ~ingなのか、そして「持つ」という状態動詞なのになぜingなのか疑問に思った方もいるかも知れません。結論から言うと、havingは分詞構文です(なので基本はgroup by句の後に記述しますが、その直前に記述しても動いたりします)。分詞構文は動詞を~ingと進行形で表し、並行して起きている状況を補足する、接続詞のような役割を果たすことができる、主に文章表現で好まれる用法です。
つまりグループ化させる命令文において、状況の補足(専門的には付帯状況)を示しているので、上記の場合、冊数が10以上のときの、という条件を補足しています。
ちなみに 分詞構文は前後の因果関係さえつながればOK ですので、今回は「条件」で解釈しています。
結合
結合は A join B on 条件が基本で、2つ以上のテーブルから共通する値を取得するのに頻用します。
inner join
inner join(単純にjoin)とは等価結合のことで、同じ条件を持つカラムを結合したい場合、よくある例ではマスタにしかない値をトランザクションから取得したい時に用います。後述するouter joinとの違いは、null値は除外される点ですが、これも英文法で解釈すれば迷うことなく理解できます。
例:書籍マスタと出版社マスタから、書籍名と出版社名を取得(書籍マスタの出版社は名称ではなく、コード名のpub_codeとなっている)
#select b.name,p.name from MstPublisher p join Mst.Books b on MstPublisher.id = TrnPublisher.pub_code
接続の用法joinは何を意味しているのか。そして、なぜonで条件を示すのか?
まず joinは他動詞で、対象を「加える」 という意味を持ちます(「つなぐ」という意味ではありません。それならば、必ず前置詞toが必要ですし、等価関係になってしまいます)。また、加えるという行為は、左右のいずれかに主従関係が発生し、主の方がメインテーブルとなることを意味しています(基本は左側が主、右側が従となります。これは英語の大前提として重要な物事ほど左側に置かれる決まりを踏襲しているのかも知れません)。
それを踏まえinnerは内部的に、outerは外部的にという副詞ですが、これも内部結合、外部結合をイメージしやすくなります。内部的に結合、つまりA inner join Bだと主のAがBのうち、AとBが共通で持っている部分のみ加えるという意味なので、Bだけが持っている情報は含まないということです。
対してA outer join BはBだけが持っている外部的な部分も加えるという意味です。ですが、主従関係が存在するので、Aにない情報の場合はNULLで表示されるようになります。
onは(上に限らず※)物事に密接してという位置関係を示す前置詞です。先程のinが中に含まれるという感覚的な意味合いなのに対し、2つのテーブル結合に際し密接に関係しているものというイメージで捉えたらいいでしょう。なのでon以下には結合条件の共通カラムなどを記述します。
かつて勘違いが多かった事柄ですがinが中にだから、onは上にではありません。壁でも天井でも対象に接していればonです(結果的にonが上を指すことが多いのは重力の関係で、物は上に接していることが多いからです。また対義語で考えると明白でonの対義語がoffなのに対し、inの対義語はoutなので、inとonに直接の関連性はありません)。
outer join
outerjoinとは外部結合のことで、left outer join(left join)、right outer join(right join)などがありますが、早い話、加えられる側のnullありを認めるってことです。A left outer join BならばBのnull値を許容し、A right outer join BならばAのnull値を許容するという意味となります。
ちなみにoutは前置詞でinの対義語、つまりは内部感覚の対義語であり外部感覚を意味するので、これもouter joinのイメージを掴みやすくなるのではと思います。それとleftとrightですが、これはそれぞれ左へ、右へという位置関係を示す副詞です。なのでA right outer join Bとなった場合はAを右側において、左側にBを主テーブルとして加える」という意味合いになり、結果、外部結合条件の逆転を意味することになります。
order by句
order by は並び替え順を決定します。デフォルトは昇順です。
ちなみにorderは順番という名詞が知られていますが、順番に並べるという他動詞にも使え、その場合には方法を、group byと同じく、byという前置詞で表現します。また、order byの場合はカラム数に対し、1,2といった序列で記述することができますが、これもbyが手段を示しているからです。
また、ascで昇順(デフォルト設定)、descで降順となりますが、これはそれぞれascending(昇順から)、descending(降順から)を意味します。
※Select文ではほかに、テーブルに対し論理和を求めるunionや、条件分岐のwhenなどもあり、これらもどこまで英文法で解釈できるかは調査中です。
insert文
テーブルへの挿入、すなわち新規登録に用いるinsert文もintoという特殊な前置詞を使用します。ちなみにinsertは挿入する、書き込むという意味を持つ他動詞です(前置詞intoへの絡みから挿入と訳されることが多いですが、自分は書き込むから来ているのではないかと思います※)。
※書き込む場合はinを用いることが多いが、いちおう英和辞典にはintoでも使用可能とある。
intoとinの違い
intoは内部感覚を表すinに、目的への到達を意味するtoが合わさって誕生した前置詞で「~の中に向けて」という意味合いを持ちます(inは中にある内部の状況そのもの)。それを踏まえてinsert intoは
insert into テーブル名 values(...)
そのままズバリ「テーブルに書き込め」という命令…だと思われがちですが、厳密には違います。なぜなら、その後に続くvalues、これは動詞などではなく(動詞だと評価、査定するという意味で、値をつけるという意味はない)、名詞の複数形だからです。すなわち、table valuesで、テーブルの値ということを意味しているからで、直訳すると「テーブルの値(…)を挿入しろ(書き込め)」という解釈が正しいです。
※ちなみに、他動詞の後にすぐ前置詞が来ているので、英文法で考えると少し違和感を覚えますが、代名詞を省略しているのかもしれません。
update文
updateはテーブルのデータ更新に用いられる、これも頻用される命令文です。一方、実は英単語としてはかなりマイナーな動詞だったらしく、もともとは改訂するという意味で用いられていました。ですが、昨今ではIT用語としてソフトウェアなどを更新するという意味で用いられる他動詞です。
setは何なのか?
では続くsetですが、これは結論からいうと過去分詞で(setはAAA型なので変形しない)、ある表現が隠されています。setは設置する、など実に多様な意味を持つ他動詞ですが、直前に対象のテーブルが記述されています。そして隠された表現についてですが、実際はbeing set、そのbeingが省略されており、受動態における過去分詞の用法となっているので、簡潔に「設定された」と訳すれば、非常に内容がしっくりきます。
したがって、update文とは
update テーブル set カラム = 値;
「set以下に設定されたテーブルを更新しろ」という命令を送るということになります。
delete系
削除関連の命令文にはdelete文、truncate文、drop文があり、それぞれ使用用途が異なります。ここは文法こそ全くややこしくないのですが、それぞれ単語ニュアンスの相違を解釈することで、紛れなくて済むようになります。
delete文
delete文は基本的に、システム上で削除処理を行うときに用いるもので、もともとは文章の校正に対し打ち消し線を入れる、といった意味で用いられてきました。また、コンピュータでもdeleteはもともと論理削除(実際にデータを消去せずに、削除フラグなどの表示制御で操作)で用いられた単語です。
ですがSQLではwhere句を用いて削除したい条件を付与して用いる命令文であり、以下の例文だと1945年以前の辞書からデータを削除という命令になります。
# delete from MstBooks where year <= 1945 and genre = '辞書';
ちなみにdelete文は主キーに従って順番に削除処理を繰り返す命令なので、次に述べるtruncate文と比較すると圧倒的に処理が遅いです。
truncate文
truncate文は削除条件なしに、テーブルの値を全部抹消、つまりは空っぽにしてしまう命令です。システムで採用するのはハイリスクなのですが、処理が高速なのでデバッグ目的では非常に有用です。また、データを空っぽにするだけなので、カラムの型設定などはそのまま残ります。
ちなみにtruncateとは(数字などを)切り捨てる意味を持つマイナーな他動詞ですが、そこから派生してデータをバサッと切り捨てる動作をイメージするといいでしょう。
drop文
drop文は、カラムの型なども含めテーブルそのものを消去してしまう命令文です。dropとは多くの意味がありますが、ここでは消去、取り下げという意味で捉えるといいでしょう。またdeleteは基本、痕跡の残る削除なのに対し、dropは内容そのものを消去してしまい、相手には見えないようにする削除を意味します。
ちなみにdrop databaseとすればデータベースそのものも削除してしまうので、まずシステム上の命令文では用いることはありません。drop文が活躍するのはDBそのものに対するメンテナンス作業の場合です。