それが僕には楽しかったんです。

僕と MySQL と時々 MariaDB

プログラマのためのSQL第4版を読んで。〜 データベース VS ファイルシステム 〜

はじめに

訳あって今、カーネルからWebまでという非常に広い範囲を日常的に触っている。
しかし、一日の時間は24時間と決まっており自分にはコードを書く事以外にもしなければならないことがある。
だが、コードを書かないという日はなるべく作りたくない。
我儘を言うなら、限られた時間でC言語で低レイヤーをやったりPHPを使って高レイヤーをいじったり、ScalaJavaを使ってその中間も色々やってみたい。

そう思った時に、その全てを叶えるのがデータベースだと思った。

NoSQLであるRedisはC言語ベースで作られていてソースコードGithub上で公開されている。
PHPではサーバサイドとして実際にDBを操作する時がある。
Scala/Javaにはjdbcなどがある。

つまり、先ほどの我儘を全て叶えてくれる非常に魅力的な領域だった。
というわけで、そんなふわふわした理由のもとにデータベースを勉強してみようと思った。

単にデータベースを勉強するといっても、実際にDBを使うことに始まり
それらを利用するためのドライバやDB自体をソースコードレベルで理解することまでを目標にする。

その上でまずDBの使い方、特にSQLについて深く勉強したいと思ったので「プログラマのためのSQL」を購入した。
ここではその書籍を読んで勉強したことをまとめていく。

データベース VS ファイルシステム

データベース、特にRDBMSにおいては普段利用するプログラミング言語が操作するファイルシステムとはかなり勝手が違う。
データベースを操作するのにはSQLを使用し、SQLプログラミング言語と異なり独自のI/Oシステムを持たずデータの宣言、操作、制御のみを行う。

しかし、プログラミング言語にもSQLにもモデルは存在する。
そのモデルを理解することで対象を理解することが容易になる。
プログラミング言語におけるモデルとは、あまり詳しくないのではっきりとは言えないが「いくつかのプログラミング言語は数学をベースとしているため数学で実際に使用する記号群や構成を使用することができる。」といったことを指す。
SQLにおけるモデルは数学でいうところの集合(Set)としてのデータである。
数学でいう集合は、それに含まれる要素は特定のタイプに属していて順序をもたない。そして、集合に対する操作は一度に全ての要素に対して行われる。

わかりやすい例が参考文献に記述してあった。

正の整数の集合から奇数の部分集合を求める場合、答えはすべての奇数を含む単一の集合として得られる。奇数を1つずつ調べて順番に集合を作っていくようなことはしない。ただ、奇数を「2で割ったときの余りが1になる」という条件を満たす数として定義するだけだ。

このような背景があるために、分類の条件を変更したとしてもテスト可能で、かつ分類そのものも可能である。
それ故、集合を対象とする集合指向モデルは並列処理に向いている。


SQLは3つのサブ言語から構成されている

  • DDL: データ宣言言語(Data Declaration Language)
  • DML: データ操作言語(Data Manipulation Language)
  • DCL: データ制御言語(Data Control Language)

DDLはデータベースに含まれる中身を定義し、そこに含まれるデータの整合性を確保する。
前述のファイルシステムでは整合性やデフォルト値、他のテーブルとの関連性は一切定義されない上に厳密にデータの論理的一貫性を保つ機能はない。
しかしデータベースにおいてはDDLがそれらの役割持ち、DMLとDCLと共に動作するためSQLは統合された全体として扱われファイルの様に分離された一部分とは扱われない。

DMLSQLを一度でも書いたことがある人ならわかる。
SQLにおける次の操作を示す。

  • SELECT
  • INSERT
  • UPDATE
  • DELETE

DCLはここではまとめない。かなり深い概念らしくあまり解説されていなかったから。


ここまでのまとめ

  • SQLは集合をモデルにしている
  • 3つのサブ言語から構成されている
  • データベースにおけるスキーマはファイルの集合体ではなく、関連性を持つ。
  • テーブルはファイルではなく、スキーマの一部である。

エンティティとしてのテーブル

データベース、特にリレーショナルデータベースにおいてエンティティとは属性によって定義される。そしてそのインスタンスはテーブルにおける1行1行を指す。
属性とはテーブルでいうところの列であり、値はそれ以上分解不能な原始的な値であるスカラ値をもつ。
よってエンティティとしての役割をデータベースのテーブルは持つことになる。

完全に同じ構成のテーブルが2つあるとき集合的な観点からみるとそれらは同じ種類の要素を持つ集合でしかない。
これはファイルであれば許されることである。なぜならファイルはそれら自体は物理的に分類された単位であるために同一情報を異なるファイルとして保持することが許容されている。
しかし、前述の内容よりSQLにおいては集合であり区別が付かないのでそのような物は一つにまとめてしまうのがよい。

関連としてのテーブル

これは非常に簡単なことで、テーブルにおける関連とは列が一つ以上のエンティティテーブルを参照することによって成立している。
これはファイルとフィールドにはない特性になっている。

行 VS レコード

行はレコードではない。レコードはそれを読み込むアプリケーション側で定義されるものであって、それ自体はスキーマで定義される。
フィールドの名前はアプリケーション側で定義されるのに対して、行はデータベーススキーマ中で定義されるということになる。


ここで空のファイルを考える。
空のファイルは0バイト長という状態であるが、空のテーブルに関しては行は空としても列を持っていて理論的制約などを保持している。
つまり数学的意味合いの集合における空集合とは少し異なり、上記の構造をとるためたとえ空であっても別の集合として扱うべきである。

次の行にある特徴としてはテーブル内の全ての行は構造上同一な形式を取る点であり、これはファイルにはない。
ファイルであれば内部に含まれるデータの構造などは全て一致しているわけでもなく文字列であったり数値であったり情報もサイズもバラバラにしてしまうこともできるからだ。

列 VS フィールド

レコード内のフィールドはそれを読み込むアプリケーション側で定義されるが行における列はデータベーススキーマで定義される。
そして列が保持するデータ型は前述の通りスカラ値を持つ。

そして、SQLでは列は列名によってのみアクセスされる。
厳密に列名によってのみというわけではなく

SELECT *
INSERT INTO <table name>

の様な省略形は存在する。しかしこれは列名のリストを単にテーブル定義で列が定義された物理順序で展開しているだけにすぎないので内部的には列名でのみアクセスされる。
SQLにおけるNULLの使用もこれに近いものとなっている。

列があるが故に、ファイルとの違いがある。
ファイルは受け入れる内容や吐き出す内容に特に制約は無いがSQLにはその制約が存在する。
それにより、SQLが扱うものには一定の整合性が保証される。

つまりはそれが指す関係の整合性を示すことになる。
これはファイルごとに独立しているファイルシステムには無い特徴となっている。

おわりに

SQLとしての基盤的な事象をファイルシステムと比較してまとめてあったのはSQLを理解する上で非常に大事な情報であると感じた。
次はDBMSが内部的に行っているトランザクションと同時実行制御に関する章を読んでまとめていきたい。

参考文献