Uzabase Tech Blog

SPEEDA, NewsPicks, FORCASなどを開発するユーザベースの技術チームブログです。

方法より原理 〜正規化ルールとリレーショナルモデルについて〜 【理屈編】

今日は。 SPEEDA を開発している濱口です。

アプリケーションデータの永続化を担うデータストアには様々な選択肢があります。
その1つとして、リレーショナルデータベース(以下、RDB)がありますが、
RDBを選択した場合、データの容れものとしてリレーショナルモデルを選択した、という表明になります。
ひいては、このモデルを正しく使用することが生産性の観点から必要となります。
(明白な設計によるコミュニケーションや制約によるデータ不整合の回避など)
その方法の1つとして正規化ルールがあります。

正規化ルール遵守は有効か

あの星野源さんも知っているはずという、正規化ルールですが、基本情報技術者の試験範囲でもあり、エンジニアであれば少なくとも聞いたことはあり、多かれ少なかれ意識しているものだと思います。
このルールをみんなで正しく運用できればよいのですが、それにはいくつかの阻害要因があると考えています。

「あたりまえ」と軽視されがち

正規化のWikiページの解説は、厳密な定義として書かれているため読みやすくはないと思いますが、
一度理解に達すると「あたりまえのことしか言っていないな」と思うでしょう。
そう考えて安心してしまうと、このルールを積極的に遵守しようという姿勢は失われます。
また、「常識」でしかないのにルールとして厳然と存在しているため疎ましく感じることすらあるかもしれません。

「設計時のみに発生するタスク」という誤解

(ルールを軽視せず)RDBのテーブル設計を行う際に、設計の妥当性を確認するためのチェックリストとして正規化ルールを適用するのはよいのですが(付番されているためチェックリストになりやすいですね)、それで終わりではありません
設計し終わったテーブルに操作を加えるとまた新しいテーブルが生まれます。
ここで言う操作とはリレーショナル演算のことで(SQLでは例えば単体のSELECT文もそのひとつで「射影」という操作です)、
すべてのリレーショナル演算は入力と出力に同じモノを想定します(閉包性*1を持つ、と言います)。
つまり、テーブルをSELECTした結果もまたテーブルだということです。
その出力結果であるテーブルを新たな操作対象(入力)として見た際には当然、正規化ルールが適用済みであることが期待されます。
急いでさっきのチェックリストのレ点を全部消しましょう!
だいぶ疎ましく感じてきました…。

以上のことから、RDBを正しく扱うために、正規化ルールの適用はもちろん必要ですが、ただ「正規化ルールを守りましょう!」というスローガンを掲げるだけでは、効果的とは言えないと考えました。
まさに、言うは易し…ですね。

f:id:yhamaro:20200302123615p:plain
すてちまおう

正規化ルールを忘れ、モデルに導かれて、正規化ルールに至る

正規化をルールとして運用することは難しいことがわかりました。
それならばまず、原点に立ち返る意味で、一度正規化ルールを忘れ、正規化ルールが依って立つところの背景、原理を理解しましょう。
そして、その原理のみによって自然に導かれて設計を行った結果と、正規化ルールを適用したそれとが少なくとも同等であるなら、「ルール」を「運用」する必要が無くなると考えました。
あるべき姿さえ正しく捉えていれば、ルールに縛られずとも大筋で間違うことはなく、且ついつでもどこでも(基底テーブルでも、派生テーブルでも)正しい設計ができるのではないでしょうか。
その原理とはリレーショナルモデルそのものです。

f:id:yhamaro:20200302123639p:plain
こころのめでみるのだ

リレーショナルモデルとは

リレーショナルモデルのリレーション(SQLのテーブルに相当)のデータ構造はひとことで、
「属性のドメイン値の、取りうる組み合わせのすべてを規定しているもの*2」です。
なので、リレーションを設計する行為は上記に当てはめると、「…のすべてを規定すること」と言えます。

ひとことで言ってもわかりにくいと思うので順を追って説明していきます。

まず、「属性のドメイン値」について。
「属性」(SQLの列に相当)には名前と型があります。
その名前と型に規定された、取りうる値の集合がドメインです。
例えば、よく交差点で見かける車両用の信号機の色は赤、青、黄ですが、これをひとつの属性としてみてみると、
「交通信号」という属性名に、「色」という型付けがなされて { '赤', '青', '黃' } というドメインを形成します。

次に、「取りうる組み合わせのすべて」について。
仮に「ハリウッド俳優たちの代表作」というリレーションがあり、
その属性が {'俳優名', '映画名' } の2つ、
ドメイン値がそれぞれ {'ポール・ニューマン', 'デニス・ホッパー' } の2つ、
および、 {'暴力脱獄', 'タワーリング・インフェルノ', 'ブルーベルベット' } 3つだとしたときに、
以下の通り、組み合わせで6通りのタプルが設計上想定されるものとして決定されます。

俳優名映画名
ポール・ニューマン暴力脱獄
ポール・ニューマンタワーリング・インフェルノ
ポール・ニューマンブルーベルベット
デニス・ホッパー暴力脱獄
デニス・ホッパータワーリング・インフェルノ
デニス・ホッパーブルーベルベット

これで設計は終わりです(ちなみに属性名の下線は一意キーを表しています。上記だと { '俳優名', '映画名' } の複合キーです)。
あとはこの属性の設計が表している、下記の述語に対して、真の命題(SQLの行に相当)を入れていくことになります。

ハリウッド俳優、 (俳優名) の最高傑作は『 (映画名) 』である

デニス・ホッパーについては『イージーライダー』をまだ観ていないので、この中から最高傑作を選ぶことは出来ません。
また、『ブルーベルベット』にポール・ニューマンは出演していないのでこれも真の命題になりません。
『タワーリングインフェルノ』と『暴力脱獄』ですが、私は圧倒的に後者の方が好きなので、以下のタプルのみ真の命題としてこのリレーションに入れることにします。

( 'ポール・ニューマン', '暴力脱獄' )

これを入れると、述語に当てはめて、以下の命題が真であることを世界に表明したことになります。

ハリウッド俳優、 ポール・ニューマン の最高傑作は『 暴力脱獄 』である

他の5つのすべて組み合わせについては、"未表明"などというあいまいな扱いではなく、偽であることを表明したことになります。(閉世界仮設に基づいています)

ハリウッド俳優、 ポール・ニューマン の最高傑作は『 タワーリング・インフェルノ 』ではない
(残り4つも同じ)

また、命題は事実について述べているので重複しません
大事なことは2回言ったほうがいいかもしれませんが、事実自体は変わりません。
あと、複数の命題間で順序はありません
大事なことは先に言ったほうがいいかもしれませんが、どちらでも事実自体は変わりません。

リレーションとその設計、そこに値やタプルをマッピングすることが何を意味しているかを述べました。
また、リレーションは先述したリレーショナル演算子が処理できるものである必要があります。
リレーションとリレーショナル演算子がリレーショナルモデルを構成します。
リレーショナルモデルがどんなものであるか、おおよその雰囲気を掴んでいただけたかと思います。

この続き

実践編では、上記の理解だけに基づき、つまり正規化ルールに依らず設計行為を行ったらどうなるかを実例をもって確かめてみたいと思います。
尚、私が書いていることは『データベース実践講義』に書いてあることの理解に基づいています。正確な定義や語彙についてはこの書籍を当たるとよいと思います。

*1:テキストに対してUnixコマンドが、S式に対してLisp関数が、オブジェクトに対してSmalltalk関数が持っていると考えています。

*2:ひとつの数式で現すと、 L ⊆ X1 × … × Xk となります