さんぽみち

なにか思いついたときとかに気まぐれに更新されます。

SQLは怖くない

はじめに

SQLって要するに集合論だと聞いたのに、全然それがわからない!って人向けです。
多くの説明が手続き的に説明されており、前提知識が0の人のみがわかりやすい説明です。
せっかく分野を超えて一般的である集合論が根っこにあり多くの人が身に付けているのに、これを利用した説明がなされないのは効率が悪いと感じております。
また、情報分野の技術あるあるとして、

  1. 数学で概念が定義され名づけられる
  2. 情報で応用する際に別名を名づける
  3. 別名が浸透する
  4. 数学で名づけられた一般的な名前を知らない人ばかりになる
  5. 他分野から来た人が数学の知識を流用できない
  6. 他分野から来た人が、自分の持っていた知識に関連があると気づく
  7. 確認しようとすると、 4. より周りが説明できず、結局知識を流用できない

といった流れがあります。
この循環を断ち切る為にも、SQLを手続き的に理解している人向けでもあります。

RDBの要素

RDB(リレーショナルデータベース)は、情報の意味に関する情報を省くと複数のテーブルと呼ばれる集合を集めただけの物です。
ここへ意味に関する情報を付与するとグラフとしての性質が生まれます。
グラフによって集合を関連付け、関連付けた集合へ演算を行って新しい集合を作るといった使い方になります。
また、このグラフを表現するのに利用されるのがER図と呼ばれる図です。
今回は、テーブルについて考えてみます。

テーブル

テーブルは、タプル(レコードと呼ばれる)を元とした集合です。
レコードの構造と要素を参照する際に用いる添え字(フィールドと呼ばれる)を定義します。
直交した値の関係性を定義することから、このタプルの構造を “リレーション” と呼びます。

レコード

テーブルで定義された構造を持つタプルの値です。
“元” と同義ですが “レコード” と呼ばれます。

フィールド

レコードの要素を参照する為のインデックスを抽象化して文字にしたものです。
“主キー” と呼ばれるものもフィールドの一つです。

主キー

レコードが一意となることを保証するためのメタ的な意味を持ったフィールドです。
一般的には、これをどのテーブルの主キーであるかというメタ情報とともに他のテーブルへ埋め込むことで関係を示します。
この関係のことを “リレーションシップ” と呼びます。

データの加工方法

SQLが力を発揮するのは、データを参照するときです。
ですので戻り値がテーブルとなるSELECT文について書きます。
実行順番等の記述がありますが、実際にその処理が行われているというわけではなく、意味として何を行うかについて書いています。

SELECT文の構造

まずは基本的な要素について大雑把に説明すると、

SELECT
  写像の定義
FROM
  直積を取りたい集合を列挙
WHERE
  部分集合の作り方

となります。
実行する順番に並び替えると、

FROM
  直積を取りたい集合を列挙
WHERE
  部分集合の作り方
SELECT
  写像の定義

となり、

  1. くっつけて
  2. 絞って
  3. 加工する

という手順を行っているのがわかると思います。
SELECTの動作について、厳密には写像をフラットにするという意味もありますが今回は追及しません。

検証

九九の計算から平方数を生み出す2数と計算結果を元とした集合を作っていきます。
ただし、平方数は同じ数を乗じて作られるもののみで、2×8などは含まないとします。
実行環境は、

sqlfiddle.com

こんなのを見つけたのでここでやってみます。

テーブルの定義

元となるテーブルは、

-- row = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
CREATE TABLE row(r INTEGER PRIMARY KEY);
INSERT INTO row VALUES
  (1), (2), (3), (4), (5), (6), (7), (8), (9)
;

-- col = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
CREATE TABLE col(c INTEGER PRIMARY KEY);
INSERT INTO col VALUES
  (1), (2), (3), (4), (5), (6), (7), (8), (9)
;

です。

FROM句による直積の確認

FROM は直積を取りたい集合(テーブル)名をカンマ区切りで書きます。

FROM テーブル1, テーブル2, ...

横に並べると、数が増えた時に読みづらいので縦に並べることが多いです。

FROM
  テーブル1,
  テーブル2,
  ...

この書き方は、他の句でも使われます。
今回の確認を行うSQLは以下となります。
SQL

SELECT
  *
FROM
  col,
  row
;

結果

c r
1 1
2 1
3 1
~ 中略 ~
9 1
1 2
2 2
3 2
~ 中略 ~
9 2
1 3
2 3
3 3
~ 中略 ~
7 9
8 9
9 9

直積を取った集合が新しく作られたのが確認できます。

WHEREによる部分集合作成の確認

つぎは、この中から平方数を生み出すレコードのみを元に持つ部分集合を抽出してみます。
今回は、乗数と被乗数が等しいレコードから成る部分集合を作ります。
SQL

SELECT
  *
FROM
  col,
  row
WHERE
  col.c = row.r  -- 要素へアクセスする場合は . でフィールド名を添える
;

結果

c r
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9

乗数と被乗数が同じレコードのみを元として持つ部分集合となりました。

SELECTによる写像の確認

先ほどまで使っていた “SELECT * ” は、恒等写像を表します。

レコードそのものもタプルである為、これを繰り返すと階層が深くなり扱いづらくなります。
そこで、 今回は、被乗数、乗数、乗算結果 を要素として持つレコードとしたいので、それを定義します。
SQL

SELECT
  col.c,                -- AS を省略した場合は同じフィールド名となる
  row.r,
  col.c * row.r AS res  -- AS で新しいフィールド名を指定できる
FROM
  col,
  row
WHERE
  col.c = row.r
;

結果

c r res
1 1 1
2 2 2
3 3 9
4 4 16
5 5 25
6 6 36
7 7 49
8 8 64
9 9 81

全てのレコードについて、SELECTで指定した写像が適用されているのが確認できます。

おわりに

自分がSQLに初めて触れた時、こう説明してほしかったなぁと思っていたことを書いてみました。
また、このような理解でSQLを習得すると、JavaのStreamやC#Linqへつなげることができます。
せっかく身に付けるのであれば、その分野でしか使えないような覚え方でなく、他につながる覚え方をしたいですね!