手っ取り遅くSQLを学ぶ
手っ取り遅くSQLを学ぶ
「手っ取り遅くSQLを学ぶ」とは?すぐにSQLを使えるよう学ぶのではなく、まわりくどくSQLを理解して学んでいく方法で解説します。
「手っ取り早くSQLを学ぶ」と言ったら、よく使うSQLをなるべくシンプルな形にして、実際に試しながら、徐々に応用していく方法だと考えています。 「手っ取り遅くSQLを学ぶ」はその逆で、SQLの記法は後回しにして考え方を解説していきます。
まずはテーブル
SQLは主にリレーショナル・データベース(RDBMS)で使用されるデータ操作のための言語です。
リレーショナル・データベースは「テーブル」をデータの集合として使用しているので、SQLを学ぶには、まずテーブルを理解する必要があります。
テーブルとは、訳すと「表」なので、コンピューターと関係なくても日常でも溢れている表現方法です。 名簿であったり、一覧表など一般に、よく目にします。
SQLで扱うテーブルは、表全般の中でもう少しルールが定まったものを対象とします。
他のプログラミング言語と同じく「型を持った値」があります。数値の123や文字列の’abc’等。 この「型を持った値」を複数持つことができます。これをレコードと言ったり、「行」と言ったりします。
このレコードが含んでいる値の数と型が一致している集合がテーブルです。 通常は、単純な数合わせではなく、型が一致しているだけでもなく、同じ意味を表しています。これをカラムと言ったり、「列」と言ったりします。 このカラムには名前を付けられることが多いです。「格納」する場合は、あとで取り出す必要があるので名前が必須になります。
この集合をレコードの配列と考えても良いかもしれませんが、この時点では順序は不動なことに注意が必要です。 他のプログラミングからは、2次元配列で表される場合もありますが、プログラミング言語を理解している人は、構造体の配列と考えた方が良いでしょう。実際にSQLで操作したデータをプログラミング言語で受け取るときには、構造体の配列に変換することがよくあります。
そして、テーブルの行も列も理論上は0以上無限まで可能です。 実際には上限はデータベースの仕様やコンピューターの限界値に左右されますが、下限は0から可能です。
行が0というのは、たとえば名簿リストを作成したとして該当者が存在しないことはあるのでよくある話だと思いますが、列が0というのは理論上存在しても現実にはないので、定義はできないようにしているデータベースもあります。
この「テーブル」単位にしておけば、自分の欲しいデータを取得できると考えて作られたのがリレーショナル・データベース(RDBMS)で、そのための記法がSQLです。
SQLとは?
リレーショナル・データベースは、SQLを実行できるだけでなくテーブルを効率よく管理するためにいろいろな機能が備わっていますが、主要なSQLはテーブルを操作するため、少し切り離して考えることができます。
SQLはテーブル(複数でも可)から「1つの」テーブルを作ることを目的としています。
SQLをすでに知っている人からすると、テーブルを作ると言ったら「CREATE TABLE」と思うかもしれません。 データベースにテーブルを作るという狭い意味ではそうなのですが、その結果がテーブルになるという意味で広い意味では、テーブルを作るという目的であると言えます。
INSERT/UPDATE/DELETEなどの更新系SQL文は、実行した結果がデータベースに反映されます。
取得するSQL文と説明されることが多いSELECT文は、実際には取得ではなく、「SELECT」の意味の通り、「選択する」と解釈する方が正しいです。 「SELECT」文により選択された結果が指定されていない場合は、暗黙の了解として結果を取得します。
3つ以上のテーブルから1つのテーブルを「作る」場合、まず2つのテーブルから1つのテーブルを作成し、作成されたテーブルと残ったテーブルから、1つのテーブルを作成するといった風に考えることで3つ以上のテーブルから1つのテーブルを作成できます。
数学(というよりも算数)の「3+2+4」を「3+2」してその結果から「5+4」を計算するのと似ています。
つまりSQLはテーブルとテーブルの計算式と言えます。
JOIN
テーブルの計算式として、一番基本は算数の足し算に相当するテーブルのJOINです。 ただし、テーブルとテーブルのJOINは、むしろ算数の掛け算に似ています。
まずは列だけ考えてみます。テーブルAとテーブルXをJOINします。
テーブルA
A | B |
---|
テーブルX
X | Y |
---|
テーブルとテーブルのJOINは、双方の「列」はそのまま全部使用します。
A | B | X | Y |
---|
続いて、行のJOINを考えます。
Aテーブルの1行に対してXテーブルのすべての行に合成します。これをAテーブルのすべての行でおこないます。
A | B |
---|---|
1 | 3 |
2 | 4 |
X | Y |
---|---|
1 | 8 |
2 | 9 |
Aの1行目とXテーブルを合成
A | B | X | Y |
---|---|---|---|
1 | 3 | 1 | 8 |
1 | 3 | 2 | 9 |
Aの2行目とXテーブルを合成
A | B | X | Y |
---|---|---|---|
2 | 4 | 1 | 8 |
2 | 4 | 2 | 9 |
すべての行を合わせた結果がAテーブルとXテーブルのJOINです。
A | B | X | Y |
---|---|---|---|
1 | 3 | 1 | 8 |
1 | 3 | 2 | 9 |
2 | 4 | 1 | 8 |
2 | 4 | 2 | 9 |
(順番は決まってないので、逆にテーブルXの1行に対してAテーブルでも構いません)。
難しいことをやっているように見えますが、実際には2桁以上の掛け算で一の位をそれぞれの桁に掛けて、十の位をそれぞれの桁に掛けるようにして、合算していないだけです。
そしてJOINの結果はAテーブルの行数×Xテーブルの行数になります。2行×2行であれば4行の結果となり、100行×100行であれば10,000行の結果になります。
これが、JOINの基本形であり、現在の分類では「CROSS JOIN」と呼ばれます。
ただし、実際にこの結果が欲しいことはまれなので、「CROSS JOIN」を使う機会はほとんどありません。 実際にほしい結果を得るためには条件式が必要になります。
条件式
条件式は、他のプログラミング言語でも「if文」等で使用されるため、なんとなくわかる理解できる部分ではあると思いますが、SQLのテーブルに対しての条件式の考え方を理解しておくと、今後のSQLの学習に役立ちます。
すでにテーブルのJOINを説明していますが、テーブルの条件式はテーブルのJOINの延長で考えることができます。
まず、テーブルは0以上の行、列を持つと説明しました。そうすると1行1列のみのテーブルも可能です。 つまり1つの値(例えば 「23」という数値)は、1行1列のみのテーブルにすることができます。 「型」が必要だと説明しましたが、1つの値であれば、推測することができます。 (通常、引用符で囲まれていなければ数値として解釈し、引用符で囲まれていれば文字列として解釈できます。)
そして、ある対象となるテーブルに対して、1行1列のテーブルをJOINすると以下のようになります。
Aテーブル
A | B |
---|---|
1 | 3 |
2 | 4 |
「2」を1行1列のテーブルと解釈 |
---|
– |
2 |
Aテーブルと「2」のテーブルをJOIN
A | B | - |
---|---|---|
1 | 3 | 2 |
2 | 4 | 2 |
列はそのまま並べて、Aのテーブルに対して、それぞれ2のテーブルを合成しています。
これだけでは、条件式になっていません。ここから「2」のテーブルを「A=2の式」にしてみます。
Aテーブル
A | B |
---|---|
1 | 3 |
2 | 4 |
「A=2」を1行1列のテーブルと解釈 |
---|
– |
A=2 |
Aテーブルと「A=2」のテーブルをJOIN
A | B | - |
---|---|---|
1 | 3 | A=2 |
2 | 4 | A=2 |
「A=2」のAはA列の意味です。つまり、A列の値を代入できます。
A | B | - |
---|---|---|
1 | 3 | 1=2 |
2 | 4 | 2=2 |
算数の等記号としてはおかしいですが、プログラミング言語を学んでいる人はおなじみでしょう。「=」の条件が成立している場合は「true」真で、成立しない場合は「false」偽です。 (最近のプログラミング言語では、代入と区別するために「==」が使われたりしますが、SQLでは式と解釈される箇所が明確なので「=」で比較します。)
というこで、さらに変換が可能です。
A | B | - |
---|---|---|
1 | 3 | false |
2 | 4 | true |
この条件式がtrueの場合のみ残すのがテーブルの条件式です。
A | B | - |
---|---|---|
2 | 4 | true |
これは、AテーブルのA=2が該当する行を抽出するだけの簡単なことを、まわりくどく説明しただけです。 しかしながら、複雑な条件式もそのまま延長して考えることができます。
先程は1行1列のテーブルでしたが、複数行のテーブルでも変わりません。 前に出した、AテーブルにXテーブルによって条件式を当てはめます。求めたいのは「AテーブルのA列=XテーブルのX列」という条件式です。
A | B |
---|---|
1 | 3 |
2 | 4 |
X | Y |
---|---|
1 | 8 |
2 | 9 |
まずテーブルのJOINをした上で、条件式の列も書き出してみます。条件式を見失わないように変遷を⇒でつなぎます。A=Xを当てはめていくと最後のtrue/falseが求められます。
A | B | X | Y | - |
---|---|---|---|---|
1 | 3 | 1 | 8 | 「A=X」 ⇒ 「1 = 1」 ⇒ true |
1 | 3 | 2 | 9 | 「A=X」 ⇒ 「1 = 2」 ⇒ false |
2 | 4 | 1 | 8 | 「A=X」 ⇒ 「2 = 1」 ⇒ false |
2 | 4 | 2 | 9 | 「A=X」 ⇒ 「2 = 2」 ⇒ true |
trueのみを抽出すると、以下のテーブルになります。
A | B | X | Y |
---|---|---|---|
1 | 3 | 1 | 8 |
2 | 4 | 2 | 9 |
「CROSS JOIN」よりも意味がありそうなテーブルが作成できました。 実際に「CROSS JOIN」が使用されることは多くありませんが、2つのテーブルの共通の値を使用してJOINすることは頻繁に発生します。
JOINと条件式を組み合わせることで、自分が求めたいテーブルの多くを(全てはありませんが)作成できます。
SQL文にしてみる
ここで、これまでやってきたことをSQL文にしてみます。
まず、Aテーブルの「A=2」という条件式でテーブルを作成しました。 条件式はSQLでは 「WHERE」のあとに書きます。
つまり、
WHERE A=2
です。
Aテーブルに対しての条件式なので、Aテーブルを指定します。テーブルを指定するにはSQL文では「FROM」の後に書きます。
つまり、
FROM Aテーブル
です。
最後に、条件に合ったテーブルから、どの列を含めてテーブルを作成するかを指定します。ここでは、もともと存在していたA列とB列をそのまま指定します。列の指定は「SELECT」の後に書きます。複数並べたいときには「,」でつなげます。
つまり、
SELECT A,B
です。これをつなげて書けばよいわけですが、書く順番があり、説明した順とは逆です。
つまり、
SELECT A,B FROM Aテーブル WHERE A=2
により以下の結果が作成されます。
A | B |
---|---|
2 | 4 |
補足しておくと、SELECTの列指定がもともと存在していた列全部の場合は「*」と書くこともできます。
SELECT * FROM Aテーブル WHERE A=2
でも同様の結果になります。 「*」を指定しても条件式の「A=2」の列は含まれません。
続けて、「AテーブルのA列=XテーブルのX列」をSQLにしてみます。 条件は「A列=X列」ですが、どのテーブルの列かを明確にするために「テーブル名.列名」のように「.」でテーブルを指定出来ます。
WHERE Aテーブル.A = Xテーブル.X
です。後はテーブルは2つ並べて、列名は「*」で代用します。
SELECT * FROM Aテーブル,Xテーブル WHERE Aテーブル.A = Xテーブル.X
これで実際に以下の結果が求められます。
A | B | X | Y |
---|---|---|---|
1 | 3 | 1 | 8 |
2 | 4 | 2 | 9 |
とは言え、今どきのSQL入門書では載っていないか推奨されない書き方です。 理論上は「CROSS JOIN」が基本のため、そこから条件が追加されていくのは自然に見えますが、実際には「CROSS JOIN」が必要になることは、ほぼなく、テーブルのJOINにはJOINに関連した「条件」が付くほうが圧倒的に多いです。
そうすると、テーブルを並べた後に、それぞれのテーブルのJOINの「条件式」があるはずで、それが離れた場所にあると読むのも書くのも大変になります。
そのため、テーブルのJOINと条件式をセットにした書き方が存在します。
その書き方で書くと以下のようになります。
SELECT * FROM Aテーブル INNER JOIN Xテーブル ON (Aテーブル.A = Xテーブル.X)
テーブルを並べる書き方よりも冗長ではありますが、テーブルのJOINと条件式がセットになることで、明確になっています。