うぃろぅです。
今日も本を読みます。目指せ技術書の虫。
今日の本
SQLに関係ある人、恐らくWeb関係者の大半が読むべき本として有名な本。
「こんなDBはイヤだ」を集めた本。歴史に学ぼう。
構成
アンチパターンの種類を4つに大別している。
- 論理設計
- 物理設計
- クエリ
- アプリ開発
DB自体の設計、SQL発行の仕方、多言語からSQLを発行する際、と徐々に本体から遠ざかっていく形になっている。DBが良くても呼び出し方がね、とならないための構成。
1部 データベース論理設計のアンチパターン
1章 ジェイウォーク(信号無視)
カンマ区切りで複数データを持ってしまうやつ。
交差テーブル(交差点)を避ける行為であることから。
外部キー設定するべき箇所をVARCHAR
型にしてカンマを許容してしまったらさあ大変。
その外部キーを用いて検索、なんてことが非常に困難になる。
商品ID | 商品名 | 倉庫ID |
---|---|---|
1 | 声優パラダイス | 1,2 |
2 | 声優アニメディア | 2,3 |
3 | 声優グランプリ | 1,4 |
倉庫ID | 倉庫名 |
---|---|
1 | 大宮 |
2 | 浦和 |
3 | 与野 |
4 | 岩槻 |
例えばこう。申し訳程度の声優要素。倉庫名がローカル。
「大宮倉庫に保管している商品」を引っ張ってきたい場合が地獄。
さらにはカンマを許容するために文字列型にしているため、倉庫ID
に「春日部」みたいな文字列を入れることもできてしまってもうそれは阿鼻。
解決策は簡単で、「交差テーブルを避けてないでちゃんと作れ」ということである。
上記の例であれば保管
テーブルでも作ればいいんじゃないかな。
保管ID | 商品ID | 倉庫ID |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 2 | 2 |
4 | 2 | 3 |
5 | 3 | 1 |
6 | 3 | 4 |
こんな感じ。保管日
等は直接関係していないため省略している。
2章 ナイーブツリー(素朴な木)
掲示板のコメント欄のような「木コメ」に「枝コメ」や「葉コメ」がぶら下がる場合のコメント
テーブルがあるとして、各コメントが「親コメント」しか持っていないやつ。
「葉コメ」から「木コメ」のような2つ以上離れた関連を辿って集計する際の処理が煩雑になってしまう。
例として適当なコメントを探そうかと考えたときに思い浮かんだのが某スマホゲーWikiの某サーヴァントのコメント欄。少し前まで荒らしが横行していたが最近落ち着きを取り戻したらしい。落ち着きの定義が問われる。
それっぽい箇所を抜粋する。
コメントID | 親コメントID | コメント内容 | コメント日時 |
---|---|---|---|
1 | (null) | やっと、本当に久々に、平和なナーサリー板が戻ってきた(感涙)。記念にナーサリーちゃんprpr | 2019-07-01 03:32:33 |
2 | 1 | 真ナが妙な変異起こしてて不安だけどな… | 2019-07-01 07:38:12 |
3 | 2 | インド帰りなんだろ | 2019-07-02 09:44:10 |
4 | 2 | じゃあ、いつもみたいに消しちゃう?不出来で不要になった真ナーサリーも? | 2019-07-02 09:56:47 |
5 | 4 | 真ナーサリー「やってみろ」(ニチャア | 2019-07-02 11:30:03 |
6 | 2 | 真ナが変なのって荒しなんじゃねえの? | 2019-07-02 19:34:37 |
7 | 6 | わざわざ管理人さんの規正報告に枝付けして意味不明なこと書いているあたりそれっぽいよな… | 2019-07-03 10:16:42 |
8 | 1 | (誰か木主に、お前がprprしてるのはナーサリーではなく叔父貴だという事を教えてあげて…) | 2019-07-01 09:43:00 |
隣接リスト(Adjacency List)と呼ばれる設計。
コメントID
が2
の枝を削除しようとする場合、外部キー参照の整合性制約を満たすため、2
の子孫を辿っていって終端から逆順に削除していかなければならない。2
を最初に削除すると3
と4
と6
の親コメントID
が不正になってしまうからである。
これはちょっと大変。
解決策はいくつかある。
- 経路列挙(Path Enumeration)
- 入れ子集合モデル(Nested Set)
- 閉包テーブル(Closure Table)
全て詳しく書くのは冗長なため書籍参照。
例として経路列挙だけ書くと
コメントID | コメントパス | コメント内容 | コメント日時 |
---|---|---|---|
1 | 1/ | やっと、本当に久々に、平和なナーサリー板が戻ってきた(感涙)。記念にナーサリーちゃんprpr | 2019-07-01 03:32:33 |
2 | 1/2/ | 真ナが妙な変異起こしてて不安だけどな… | 2019-07-01 07:38:12 |
3 | 1/2/3/ | インド帰りなんだろ | 2019-07-02 09:44:10 |
4 | 1/2/4/ | じゃあ、いつもみたいに消しちゃう?不出来で不要になった真ナーサリーも? | 2019-07-02 09:56:47 |
5 | 1/2/4/5/ | 真ナーサリー「やってみろ」(ニチャア | 2019-07-02 11:30:03 |
6 | 1/2/6/ | 真ナが変なのって荒しなんじゃねえの? | 2019-07-02 19:34:37 |
7 | 1/2/6/7/ | わざわざ管理人さんの規正報告に枝付けして意味不明なこと書いているあたりそれっぽいよな… | 2019-07-03 10:16:42 |
8 | 1/8/ | (誰か木主に、お前がprprしてるのはナーサリーではなく叔父貴だという事を教えてあげて…) | 2019-07-01 09:43:00 |
こんな感じになる。
文字列になってしまうため、ジェイウォークと同じような扱いづらさがあることが欠点と言える。
閉包テーブルがおしゃれでいい感じ。深さ
も付与した閉包テーブルを作成すればノードの追加や移動があったとしてもソートしやすそう。
「絶対にこれ」ではなく各々の利点と欠点を理解して使い分けることが重要。
3章 IDリクワイアド(とりあえずID)
全てのテーブルに自動生成のIDをつけるやつ。
これは賛否が分かれる。
とりあえずid
ではなくhoge_id
のように意味のあるIDを振るのが大事。命名が大事なのはどの言語でも一緒である。
例はなし。わかるし。
4章 キーレスエントリ(外部キー嫌い)
読んで字のごとく、外部キーを設定するべき箇所に設定していないやつ。
サポートしていないなら仕方ないにしてもあるものは使うべき。
外部キー参照がないとレコードの削除時に不整合が起こる。
外部キー設定時にデータ更新が面倒になることもあるかもしれないが、整合性がないテーブルよりはるかにコストが低い。
解決策としては外部キーを設定する。そりゃそうだ。
5章 EAV(エンティティ・アトリビュート・バリュー)
DBなのに行列でデータを持とうとしてしまうやつ。
文字だけではわかりづらい。
掲載されていた例はこちら。
簡単なところでは健康測定のテーブルとして
出席番号 | 測定対象 | 測定結果 |
---|---|---|
334 | 身長 | 170.2 |
334 | 体重 | 65.5 |
334 | 座高 | 85.6 |
334 | 視力(右) | 0.8 |
334 | 視力(左) | 0.4 |
334 | 最低血圧 | 95 |
334 | 最高血圧 | 130 |
こんな感じにしてしまう。主キーは出席番号
と測定対象
の複合。
NHKとかガチャ目すごいとかちょっと足長めかなとかそういうことではない。
これをしてしまうと、「この測定結果は整数でこの測定結果は少数」のような制約をつけることができない。さらに「医師面談結果」みたいな測定対象があったとして測定結果が「異常なし」だとすると文字列になってくる。考えるだけで地獄が見える。
こういった運用をしたいのであればそれはSQLではなく別のDB管理方法をとった方が良くなる。リレーショナルである意味がなくなるからである。
今時こんなことしないやろって思っていたが以前携わったプロジェクトにこれを採用しているテーブルがあった。うーむ。
解決策としては
- シングルテーブル継承
属性名を列にする - 具象テーブル継承
属性ごとにテーブルを作る - クラステーブル継承
オブジェクト指向の継承のようにテーブルを継承する - 半構造化データ
追加や削除が多い属性をXMLやJSONで取り扱い、それを格納する列を設ける
といった方法がある。
属性の更新頻度や量などで総合的に判断すべし。
6章 ポリモーフィック関連
子テーブルに対して親テーブルの選択肢が複数ある場合に、親テーブルの名前をレコードとして持つやつ。
解決策の1つとしてポリモーフィック関連があるが、フレームワークでサポートしている等の理由ではない限り避けた方が無難。
Railsではサポートしているが、用法容量を守って正しく使おう。
親のクラスによって出し分ける、なんてあってはならない。
ポリモーフィック関連を避けた解決策として
- 参照を逆にする
- 複数の交差テーブルを作成する
- 交差点に交通信号を設置する
複合キー+そのうちの1つにユニーク制約を付ける - 両方の「道」を見る
複数の交差テーブルをjoin
する - 「道」を合流させる
union
で結合してビューにする - 共通の親テーブルを作成する
という手法がある。4以降はどれかを選択する。
共通の親テーブルを作成すれば良さそうに見える。Kernel
モジュールみたいな。
7章 マルチカラムアトリビュート(複数列属性)
1つのデータに複数の同系統データが関連するとき、複数の列を作成してしまうやつ。
これも文字だけではわかりづらい。
(私にとって)身近な例で言えばTwitterのアンケート機能。
川柳少女観終わりそうなので次
— うぃろぅ (@vviilloovv) 2019年7月13日
最近投げたアンケ。シュタゲゼロ今観てるよ。消滅都市は観ると記憶が消滅するから見なくていいよって言われたけれど果たして。
閑話休題、この選択肢は2~4個までの間で可変。
このアンケートテーブルを
アンケートID | ツイートID | 回答期限 | 選択肢1 | 選択肢2 | 選択肢3 | 選択肢4 |
---|---|---|---|---|---|---|
101 | 123 | 1:00 | 消滅都市 | 五等分の花嫁 | シュタインズ・ゲート ゼロ | スター☆トゥインクルプリキュア |
こうしちゃダメだよということ。
(この例だとやらなさそうだけれど)値が「消滅都市」の選択肢を検索する際、選択肢1~4を全て検索しなければならないため、効率が悪い。
更に選択肢を2つのみにする場合、値が入らない箇所はnull値となる。わかりやすい感想としては気持ち悪い1。
また、選択肢を5個まで増やせるように機能が変更されたとしたらテーブルに列を追加する必要が出てくる。これはとてもコストがかかる。
解決策は従属テーブルを作成する。
この例だと選択肢テーブルを作れば良い。
ジェイウォークと同じような対処法となる。
8章 メタデータトリブル(メタデータ大増殖)
「メタデータ」はテーブルや列のこと。行はデータ。
これがトリブルのように無限増殖すること。海外のTVドラマに出てくる毛玉みたいなキャラクターの名前らしい。
年ごとや月ごとににテーブルを増やす、列を追加する、とかそういうこと。
テーブルの名前を変えてコピーする場合、コピー元のテーブルにバグがある場合そのバグごとコピーされることになる。列に変更がある場合は全てのコピペ先に適応しなければならない。コピペプログラマみたいだな…。
過去データを分離して保存しておく場合は例外的にこれもあり。変更しないで参照するだけ、とか制約を付けておくとなお良し。
解決策はパーティショニングと正規化。
RDBMSがサポートしているパーティショニングを用いてテーブルを分割できる。
物理テーブルが分かれていたとしても内部でうまいこと振り分けてくれる仕組み。
1部は以上となる。プロジェクトで見た覚えのあるアンチパターンがいくつかあって心が痛い…。
2部 データベース物理設計のアンチパターン
9章 ラウンディングエラー(丸め誤差)
小数を扱う導出項目のデータ型をfloat
にすると、精度が落ちてしまう。
JavaやC#ではDecimal
型があるためそれを使えば概ねなんとかなる2。
解決策はnumeric
やdecimal
を使う。SQLにもあるのね。
正確な計算はどの言語でも気を遣うよね。金融系のシステムなんかでは必須。
10章 サーティワンフレーバー(31のフレーバー)
特定の値しか入れたくない列があるとする。例えば[大宮 浦和 与野 岩槻]のどれかしか入力できないようにしたい、みたいな。
この値を「列を定義するタイミング」で指定してしまうのがアンチパターン。
サーティワンフレーバーというのは、バスキン・ロビンス社3が当初は「月によって変化する31のフレーバーの中から好きな味を選べる」していたが、現在では31を超えて数多くのフレーバーを同時に取り扱うようになっていることから。
新たに選択肢を追加する場合は列の修正が必要となる。
配列やハッシュ等を使わずにインスタンス変数を新たに宣言するイメージ。
修正や削除のときも同様。
解決策は外部キー制約を付けて選択肢をデータ管理すること。
create table さいたま市構成市名( 旧市名 varchar(20) primary key ); insert into さいたま市構成市名 values('大宮'); insert into さいたま市構成市名 values('浦和'); insert into さいたま市構成市名 values('与野'); insert into さいたま市構成市名 values('岩槻'); create table 住所( (何か他の列), 旧市名 varchar(20), foreign key(旧市名) references さいたま市構成市名(旧市名) on update cascade );
こんな感じ。テーブル名とか列名はわかりやすさ重視のため日本語とした。
これなら「やっぱり与野は外そう」と考えてもすぐ外せる^4し、「上尾と蕨も合併しよう」と考えてもすぐに追加できる。
11章 ファントムファイル(幻のファイル)
まず名前がかっこいい物理ファイル自体をレコードとして追加するのか、物理ファイルのパスをレコードとして追加するのか問題。
たびたび議論の対象となるが、この本の筆者の立場は反対。
理由を挙げる。
- レコード削除時に外部ファイルが残ってしまう
- 物理ファイルを削除や変更した場合、トランザクション管理できずに変更が他ユーザーに反映される
- (ファイルも同時に削除するとして)削除処理をロールバックする際に物理ファイルはロルバ対象外のため削除されたままとなってしまう
- RDBMSのバックアップにDB外の物理ファイルを含めることが困難
- 外部ファイルに対するアクセス権限はDB内の権限とは別物
- ファイルはデータ型ではない(パスの場合DB内では文字列型となる)
なるほど。こう見ると結構ある。
逆に言えばこれらへの対応がしっかり練られていればそれはそれで問題ない。
解決策はblob
型を採用すること。
最近のDBは容量に余裕があるのである程度のファイルサイズなら許容できるでしょ、という考え方。
ファイルの読み込みをサポートしているRDBMSも多いため、不自由には感じないはず。
12章 インデックスショットガン(闇雲インデックス)
禁書目録さんがショットガン片手に近づいてくる光景が脳裏をよぎったが絶対違う。
DBのパフォーマンスを向上させるために有効な手段の一つとしてインデックスを効果的に使用することが挙げられるが、その効果的な使用法について。
名簿であれば「あ行」「か行」みたいな。
ソート済みでなければインデックスは機能しないため、データ追加や更新のたびにソート処理が走る。そのため定義するとオーバーヘッドは生じる。
そのオーバーヘッドを上回る効用があるよねと。
だからといってたくさん貼ればいいわけではない。これが今回のアンチパターン。
例えば以下はバッドプラクティス。
- IDにインデックス
ほとんどのRDBMSは主キーのインデックスを自動的に作成するため冗長。 - 説明文等の長い文章にインデックス
これを対象にselect
する機会があまりなく、オーバーヘッドの方が大きくなりがち。 - 所要時間などの付帯要素
こちらも検索条件になりにくい4。 - 複合インデックス
利点はあるが、定義した順番どおりに使用しなければ意味がなかったり、使用頻度が低くなりがちだったりと欠点も多い。
解決策は適切なインデックスを作成すること。わかりやすい!!
…で、どうすればいいのよと。そりゃそうなる。
適切なインデックス作成のためのチェックリストとして「MENTOR」が挙げられている。
- Measure(測定)
ボトルネックを調べる。 - Explain(解析)
クエリ実行計画(後述)のレポートを取得して解析する。 - Nominate(指名)
解析結果から原因を特定する。 - Test(テスト)
修正が適当か確認する。 - Optimize(最適化)
キャッシュに割り当てるメモリ量を調整し、頻度の高いインデックスをキャッシュにロードする。 - Rebuild(再構築)
定期的にメンテナンスする。
の頭文字をとったもの。
クエリ実行計画って?
Query Execution Planの頭文字をとってQEPとも。
クエリ実行にどのインデックスを使うかを判断する計画のこと。
RDBMSによって多少差はあるが、概ね
- クエリに関連するテーブル
- インデックスの選択理由
- テーブルへのアクセス順序
が含まれる。
RDBMSごとに何が吐かれるかを調べるが吉。
2部は以上。インデックス張ってないDBは見たことがある…。
3部 クエリのアンチパターン
13章 フィア・オブ・ジ・アンノウン(恐怖のunknown)
名前がかっこいい。
住所の「マンション名・部屋番号」のように入力必須ではないデータを含む結果を得ようとする際、null値を一般値のように扱うことがアンチパターン。またはその逆。
null値を使うことがアンチパターンというわけではない。扱い方が問題。
SQLではnullを特殊な値として扱う。0
でもfalse
でも空文字でもない。
上記の例で住所を結合して出力したい際、「番地」と「マンション名」を結合しようとしたときに「マンション名」がnullの場合出力はnullとなってしまう。
いやー困っちゃうね。
だからといって自然数が入ることが想定されている列に-1
を入れるようにすると集計処理の際-1
を除く処理をしなければならなくなる。うおお面倒…。
null == null
の結果がnull
になる理由は「年齢不詳の人と年齢不詳の人が同い年かどうかはわからないでしょ」である。わかりやすい。
解決策はnullを考慮した熟語を用いること。
is null
is not null
is distinct from
is distinct from
はサポートしていないことがあるため注意が必要。
あとはnot null
制約をつけることだろうか。無論全てにつくわけではないが。
14章 アンビギュアスグループ(曖昧なグループ)
group by
を利用してグループ内の最大値や最小値といった値を取得し、その行の他のデータを取得したい際に、グループ化していない列を参照すること。
以前書いたSQL文で最初そんなことが起きていた。
これはサブクエリを使うのでは?と期待しつつ読み進める。
グループ化していない参照値がユニークでない場合にこういったことがある。
SQLの仕様理解が大事。相手の気持ちを汲み取っていこう。
関数従属性がある列になら使っても良い。この言葉が出てくるのは第二正規系の時だっけ…?
助けて過去の私。
しかも第三だった…。ちなみに第二は複合キーを分ける。
解決策はいくつかある。
- 関数従属のある列のみにクエリを実行する
- 相関サブクエリを使用する
- 導出テーブルを使用する
- 外部結合を行う
- 他の列もグループ化する
- グループごとにすべての値を連結する
いっぱいある。それだけ多くの人がこのアンチパターンにお世話になったということだろう。かく言う私もその1人であることだし。
15章 ランダムセレクション
ランダムな値が必要とされる際、ランダムにソートした最初の行を取得する。
少量のデータをランダムに取得するだけなのに全体をランダムソートする、というだけで大変なことをしていることがわかる。
データが少ないなら問題ない。それが増えないならなおのこと。
解決策はいくつかある。
- 1から最大値までのランダムなキー値で抽出
主キーが1から始まり、欠番がないことが条件。論削する方針のDBならいけそう。 - ランダムなキー値で抽出 + 欠番の場合その次の値で抽出
物削する方針のDBならこちら。 - 主キーのリストをアプリ側で取得し、その中からランダムに1つ抽出
リストが大きくなりすぎないのであれば有用。2回DBとやりとりするので若干冗長かも。 - オフセットを用いてランダムに行を選択する
ヒットした件数のうち何番目をとってくるか、をアプリ側でランダムに指定してとってくる。良さそうだがlimit
をサポートしていないことがあるので注意。 - ベンダー依存の解決策
RDBMSにサンプル機能があるならそれ使えばいいよね。
乱数周りはパフォーマンスに影響が出やすい。よく調べて実装するのが吉。
16章 プアマンズ・サーチエンジン(貧者のサーチエンジン)
貧者と見るとカルナさんが思い出される。インド異聞帯最高だったよ。
全文検索を行う際、パターンマッチ述語を使用する。
正規表現は便利だが、データ全体に対して行うとデータ量に比例してパフォーマンスが低下してしまう。
単純なデータに正規表現を用いる分には問題ない。積極的に利用していこう。
最善の解決策はSQLを使わずに全文検索エンジンを使うこと。
次善の解決策は検索結果を保存して使いまわすこと。
これに関してはRDBMSを頼るのが良い。全文検索することは多いだろうからだいたいあるはず。
全文検索が必要となったときに調べる、ということが必要ということを覚えておくのが大事。
SQLが苦手なことはSQL以外にやってもらう。餅は餅屋精神。
17章 スパゲッティクエリ
SQL世界のスパゲッティプログラム。まぁどこの世界にもあるよね。あってほしいわけではないが。
合言葉は「全部1回で解決しようとするな」で。
とりあえず結合、としてしまうと場合によってはクロス結合することがある。
データ量が多いテーブル同士のクロス結合は地獄。
アンチパターンを用いても良い場合、という箇所で「上司を説得して複数に分けろ」と書いてあったのが面白い。要は使うなと。
解決策は処理を分けること。単一責任。
18章 インプリシットカラム(暗黙の列)
「暗黙の」ってついている時点でいやな予感しかしない。
ワイルドカード指定や暗黙の列指定(insert into
の列指定をしない等)を行うこと。
例えばinsert
文の列指定を省略した場合、列追加をするとinsert
文がエラーになる。列の数と値の数が合わないからである。列を削除した場合も同様。
怠惰と保守性とのせめぎあい。書き捨てではない場合さぼらず書こう。
解決策は列名を明示的に指定すること。
サンプル以外でSQLを発行する場合はワイルドカードを使用しない、と制約を付けてもいいかもしれない。
クエリのアンチパターンは以上。 スパゲッティクエリだけは絶対にダメ。
4部 アプリケーション開発のアンチパターン
19章 リーダブルパスワード(読み取り可能パスワード)
パスワードを平文で格納する。
絶対ダメ。攻撃された場合はもちろん、悪意のある内部の人間が絶対にいないとは言い切れない。
また、認証時にも平文同士で比較することになり、たとえユーザーが間違ったパスワードを入力していたとしても非常に危険となる。他の認証に使いまわすことがあるからである。
解決策はパスワードのハッシュにソルトを加えて格納すること。
パスワード文字列を不可逆な暗号化文字列に変換して比較するようにすれば良い。
不可逆な変換の簡単なものは剰余の計算である。
(適当な数値) % 3 = 2
であるとき、この適当な数値が何かわからない。5でもいいし8でもいい。「結果から初期値がわからない」のが不可逆な変換。
ソルトは適当な文字列のこと。ユーザーのパスワードに適当な文字列を付与してからハッシュ化することで元の文字列がさらに判別しにくくなると言う寸法。
この辺の技術に関しては「体系的に学ぶ安全なWebアプリケーションの作り方」に詳しく載っているらしい。この前買ったから今度読む。
また、ハッシュ化する処理をSQLで行う場合、平文で扱う瞬間が存在してしまうため、アプリ側でハッシュ化する処理を実装したい。
また、パスワードがわからなくなった場合はリカバリーするのではなくリセットするのが絶対。
詳細は上記の本を読むべし。
20章 SQLインジェクション
情報処理試験でもよく見るあれ。ユーザーにSQL文を発行されて実行しちゃうやつ。
select * from Bugs where bug_ig = (入力された値)
という動的SQLを発行する際、入力値が1234; delete from Bugs
だった場合に
select * from Bugs where bug_id = 1234; delete from Bugs
というSQLが発行され、Bugs
テーブルの中が消し飛ぶ。
解決策はいくつかあり、どれか1つで全ての攻撃が防げるわけではない。
- 値のエスケープ
文字列型の列には有効。 - プリペアドステートメント
絞り込むパラメータが単一の際は非常に有効。
列名や識別子等も入力されたパラメータは文字列として扱うため、select
以外で使いたい場合想定した結果が得られない。 - ストアドプロシージャ
安全とは言い切れない。 - データアクセスフレームワーク
安全とは言い切れない。
状況に応じて適切に使い分けることが重要。
誰も信用してはならない。
「誰も」には自分自身も含まれている。
充分にテストされた関数のみを使い、自分で実装しようとは思わないことを意識するべきである。
21章 シュードキー・ニートフリーク(疑似キー潔癖症)
連番で振っているIDに欠番が出た際に欠番を別のデータで埋めちゃうやつ。
キモチワルイ気分になるのはわかる。わかるがやらない方がいい。
欠番に新しいデータを入れていく場合でも欠番を詰める場合でも、同時に2つの命令があった場合処理ができなくなる。
また、振りなおす前の主キーをどこかが実数値で持っていた場合、そことの整合性がおかしくなる。
解決策は欠番を埋めないこと。そりゃそうだ。
重要なことは上司を説得すること。コミュニケーションだ。
22章 シー・ノー・エビル(臭いものに蓋)
デバッグする際、自分のコードは正しいと思い込んで戻り値やエラーメッセージを無視すること。
「よくわからないけど動かないからバグ」みたいな。
エラーメッセージをよく読もう。
あとは使う前にドキュメントをよく読もう。
解決策は例外と戻り値をしっかりチェックすること。
それとステップ実行。どこからおかしくなるかを特定できれば対応も素早くできる。
23章 ディプロマティック・イミュニティ(外交特権)
アプリのソースコードにおいてはベストプラクティス5を採用することを良しとするが、SQLを特別視してSQLにはベストプラクティスを採用しない。
「DBはアプリとは別」という考え方があることに起因している。
アプリ設計者とDB設計者が別だったり、DB用のIDEが広く普及していなかったり、アプリとインフラの部門が分かれていたり。原因は色々挙げられる。
かく言う私の会社も「アプリ」と「ITインフラ」で部が分かれている。なるほどこれかぁ。
書き捨てならこの限りではない。このパターンいくつかあるな。
解決策はDBも品質管理の対象に入れること。
- 文書化
- バージョン管理
- テスティング
- ブランチ管理
24章 マジックビーンズ(魔法の豆)
MVCの全てのモデルクラスがアクティブレコードを継承するべき、という考え方。
便利な機能を全員が使えるようにすることでジャックの魔法の豆のようにいい感じにプロジェクトが育つなんて気のせいだよ、みたいな。
どこからでもDBを触れると逆にどこに任せればいいかがわからなくなっちゃうよ…ということなのかな。ちょっと私の理解が足りていない。
解決策はモデルにアクティブレコードの管理を任せること。
ドメインモデルの責務とすることでDBの取り扱いを単一責任にするのが大事。
シンプルに取り扱う方法を追求していくといいんじゃないかな、といったところ。
25章 砂の城
想定不足。砂上の楼閣。「これが起きたら大変なことになる」に対する対応策がない状態。
対応策は知識を付けてちゃんと検討すること。
例外処理やバックアップ、ベンチマークテストの実施等を徹底しようね。
おわりに
読んでいると割と「あーあったあった」ってなってしまった。
この本を読むことによって「ジェイウォークにあたるからこの設計はやめましょう」のような指摘をしっかりとした根拠の上でできるようになった、ということが一番大きな収穫といえる。
デザインパターン同様、共通認識として会話するためには最高の良書。
逆に一番問題なのは私の周囲の人がこの本を読んでいないこと
会社が買ってくれればいいのだけれど。まぁ無理よね。次は何を読もうかね。
ではまた。