分析用途のテーブルにnullはいらない
nullableはやめとけ?
nullをデータベースでどのように扱うかは、非常に難しい。そもそも、nullとは何なのか。これは、データが存在しない(null)こととゼロを区別する方法として追加されたものになる。
業務システムにおいては、nullは必要であると誰しもが答えると思う。一方で分析システムではどうだろうか?本当に必要だろうか?について考えをまとめてみた。
分析システムにおいてnullはデメリットの方が大きい
結論から書くと、分析システムにおいては、nullを使うとクエリをする度に、気をつける必要があり、もし間違いが発生していても気づきにくい。逆にnullがあって助かったという場面があまり思いつかないくらいにはnullが嬉しい場面が少ない。(分析システム、業務システムについては、こちらの記事を参照 )
では、nullの代わりに何を使うべきか?それは、intなら0、textなら空文字やnullを表す文字列を格納すれば良いと考えている。一般的な業務システムのデータベース設計に慣れている人からすると、なるほど?????という感じだと思うが、これは現実的な解である。
nullは値ではない
nullが複雜さをもたらすのは、nullが値ではないからである。
nullはその性質上、他のレコードに存在するnullと同じであると解釈されない(null = null
は常にfalse)し、逆に他のnullと違うということも解釈(null <> null
は常にfalse)が出来ない。これによってどのような複雜さが発生するか見ていこう。
例えば、顧客管理システムがあったとする。顧客テーブルにnullableな会員グレードを示すカラムがある。このカラムには、グレードなし、またはブロンズなどのグレードを示す値が入る。しかし、何らかの理由でnullが入っていることもある。これはグレードなしと同じ様な意味を持っている。
このテーブルを使って、グレードなし以外の顧客で絞り込んでレポートを作成したいとする。nullが比較できないことを知らない開発者は恐らく比較演算子を使って次のようなクエリを書く。WHERE グレード <> 'グレードなし'
、しかし、これをやってしまうと、グレードにnullが格納されている顧客が含まれない。そこで登場するのが、IS NULL
である。WHERE グレード IS NULL OR グレード <> 'グレードなし'
とするとほしい結果が得られる。ここからさらに追加の比較が入ってくると、括弧が登場しクエリがややこしくなる。
これだけでも大変だが、複雜さはクエリだけではなくコミュニケーションにまで波及する。nullの顧客はどのように扱うのか?そもそも、nullとは何なのか・・・余計なコミュニケーションコストがかかってくる。何故このようなことが起きるのか?それはnullには意味がないからである。
外部キーにnullがあると結合クエリが辛い
業務システムのテーブル設計で外部キーにnullを入れるケースはあるだろう。これによって分析クエリは難しくなる。
例えば、aテーブルとbテーブルに、codeというnullableなカラムがあったとする。codeがnull同士でJOINしつつ、値が同じものをJOINする時のクエリがかなり悲惨なことになる。
a.code = b.code OR (a.code IS NULL AND b.code IS NULL)
仮にusing(code)
や a.code = b.code
でJOINをすると、nullが含まれるレコードは全て消え、本来ほしかった結果を得られなくなる。しかも、エラーにはならない。
nullは難しい
nullを扱うのは難しい。仮に全て回避出来たとしても、クエリが複雜になることは必死だろう。
もし、nullを無くなれば、ここまで書いたような複雜さは回避される。
nullを表す特別な値を入れる
一番簡単な方法として、nullの代わりとなる値を入れるという対応がある。例えば、売上が決まってないのであれば、0を入れればいいし、商品名が決まってないのであれば空文字やnullを表す別の文字列でも良い。だが、この方法は一般的なリレーショナルデータベースのテーブル設計の原則に違反している。商品名が決まってないのであれば、それはnullを入れるべきである。だが、分析用途で起きる問題を解決するためには、必要な妥協になりえると考えている。しかし、この対応には、一つ問題がある。この空文字はどういう意味を持っているのか?という疑問が出てくる。これは空文字が入力されたのか、それともnullだったものが空文字として入っているのかが分からない。これを解消したい場合は、特別なレコードを用意する必要がある。
nullを表す特別なレコードを別途作成する
図1
図1の様なテーブルを作成し、明示的に値がないことを示す特別なレコードを作成する。これにより、WHERE salesrep.row_type = 'salesrep'
や WHERE salesrep.row_type = 'no salesrep'
で絞り込みが出来る。これで先に書いたようなこの空文字はどういう意味なのか?とはならないだろう。
この特別レコードを使った方法を応用するとをすると、ETL過程で検知した不正なデータに対しては、row_type
にInvalid
として明示的に不正なデータを表すことも可能だろう。もちろん要件次第だが、必要であればやると良い。
まとめ
分析やレポート関連で、nullはどこまで価値があるんだろうか?色々操作して最後に出てくる表にnullはあるのか?
もしかしたら、そのソースとしているテーブルのカラムは全部not nullでいいかもしれない。
Discussion