👻

RDBは集合、SQLは集合の操作

2024/05/04に公開

この記事について

https://zenn.dev/levtech/articles/778ab92d4d217b

という記事がZennのランキングに載っていて、X (Twitter) 上で感想を書いたところ執筆者の方と少しだけ会話させていただけた。

https://twitter.com/miketako3/status/1786015956898386303

その中で、わりとDB周りを見る経験があった自分が持っているRDBやSQLへの認識をシェアしておくと、他の方にも参考になるかもしれないと思ったので、この記事で軽くまとめておく。

RDBとSQL

Wikipediaには以下のように書かれている。

https://ja.wikipedia.org/wiki/関係データベース

関係データベース(かんけいデータベース、リレーショナルデータベース、英: relational database)は、関係モデル(リレーショナルデータモデル、後述)にもとづいて設計、開発されるデータベースである。

関係モデルは、IBMのエドガー・F・コッドが考案し、現在もっとも広く用いられるデータモデルである。データベースの利用者は、クエリ(問い掛け)をデータベースに与え、データを検索したり、変更することができる。

データは表に似た構造で管理されるが、関係(リレーション)と呼ぶ概念でモデル化される。関係(リレーション)は組(タプル、表における行に相当する)、属性(アトリビュート、表における列に相当する)、定義域(ドメイン)、候補キー(主キー)、外部キーなどによって構成される。

はてなんのことやら、という気分になる人が多いだろう。

私の認識としては、RDBは「集合」を管理するものである。

RDBの行は各カラムの元を一つずつ要素に持つ組 (Tuple) の集合であり、その集合を良い感じに結合したり、抽出したりするのがSQLである。

そして、SQLでは集合の部分はカラムの型と数が合っていれば別の集合で置き換えられる。

具体例

例えば、以下のようなusersテーブルがあるとする。これが意味するのは、「users集合の元はidnameageの元をそれぞれ一つずつ持つ組」ということである。

id name age
1 Aさん 5歳
2 Bさん 10歳
3 Cさん 15歳
4 Dさん 20歳
5 Eさん 25歳

この時、以下のようなSQLを実行することで、age5歳以上のユーザーを抽出できる。

SELECT * FROM users WHERE age >= 5;

ここで、5は正確には「INT型が1個だけ存在する1行だけの集合」であるから、別の「INT型が1個だけ存在する1行だけの集合」で置き換えられる。

例えば、このテーブルの最年少の年齢を取得するSQLと置き換えてみよう。これは年齢が一つだけ返ってくるから、型が合っているので置き換えられる。

SELECT * FROM users WHERE age >= (SELECT MIN(age) FROM users group by id);

これは構文としては正しく、実行するとageテーブルの最小値以上のユーザーを抽出できる。

同様に、例えばusersテーブルも集合である。こちらもAさんを除外する形で置き換えてみよう。usersの部分は「idnameageの元をそれぞれ一つずつ持つ組」の集合であるから、id1以外のユーザーを抽出するSQLに置き換えても型が合っているので成り立つ。

SELECT * FROM (SELECT * FROM users WHERE id != 1) WHERE age >= (SELECT MIN(age) FROM users group by id);

これも構文としては正しく、実行するとAさんを除いたテーブルについて、ageテーブルの最小値以上のユーザーを抽出でき、最終的にはBさん以降が出力されるはず。

もちろんさらに集合に置き換えて複雑なSQLを作ることもできる。

まとめ

RDBとSQLは集合を扱うものである。

集合の部分は、型と数が合っていれば別の集合で置き換えられる。結局意識すべきは型なのだ。

これを意識してSQLを書くと、よりSQLで書ける世界が広がるかもしれない。

なお今回はクエリのパフォーマンスは一切考慮していない。あくまでサブクエリって自分はこういう認識だよを伝えるためだけの記事です。

Discussion