X(Twitter) Zenn GitHub RSS 共有

データベース・リファクタリング

作成日時:2025-12-09
更新日時:2025-12-11

データベース・リファクタリングを読んだメモ。

全体

全体の感想としては、当り前のことを書いているな、ぐらい。
マルチアプリケーション・データベースアーキテクチャの場合の移行手法はためになったが、
そういうアプリを扱うことは個人的にはなさそう。
DBアクセスはAPIサーバーのみが管理するだろうし、それより大規模になればマイクロサービスで、
各APサーバーがそれぞれ単一のDBを参照するだろうし。

トリガーやストアドなどを多用しているが、個人的には好みではない。
DBにビジネスロジックを持たせてはいけない。
変更/管理がしづらいから。
APは簡単に増やせるし変更もできる。デバッグ/テストも楽だし、DBにロックインされない。

これらはリファクタリングやデータ移行のための手法と考えている。
もしくは監査(トリガーでDB操作を登録)

もしくは極限までパフォーマンスを高める場合。
全部ストアドにすればingresは減るよね。
SQL全文渡す必要ないし。
マルチにおいてはシステム全体におけるDRY原則順守とも言えそう。

テーブル継承

STIはテーブル制約が煩雑になる。
NOT NULLやユニーク制約を付けることができない。

似通った内容を1カラムにまとめたがるが、1カラムに複数の意味を持たせてはならない。
会員→個人/法人でSTIの場合、創業日と誕生日を同一カラムに持たせたりなど。

具象TIは情報が複数テーブルに跨るからユニークを担保できない。

スキーマと設計

ANSI/SPARC 3層スキーマアーキテクチャ

下位スキーマの変更は上位スキーマに影響を与えない。

設計

ストアド

プロシージャ

複数の処理をまとめて行える。
SELECT/INSERT/UPDATE/DELETEなんでも。

ファンクション

単一値を返す。
SELECTのみ。

ドキュメント

P.11 1.6

当時は「書いては直し」(code-and-fix)の開発方法が一般的であった。
ITコミュニティは、この方法では質が低く保守しにくいコードしかできないと判断し、

今のvibe codingみたい。

リファクタリングにおけるデータベースアーキテクチャ

シングルアプリケーション・データベースアーキテクチャ

1つのアプリのみがDBを使う。
この場合は、データベースを自由にリファクタリングできる。
影響範囲が分かる。
アプリケーションをリファクタリング内容に合わせて修正すればいいだけ。

マルチアプリケーション・データベースアーキテクチャ

複数のアプリがDBを使う。
こっちは気軽にDBのリファクタリングができなくなる。
書籍「データベース・リファクタリング」はこちらをどうするべきかを書いている。

情報を2つ持ち、それらを同期させる(トリガーを使用)。
古いほうが参照されなくなったら、それを削除。
というのが、本の大部分で書かれていたリファクタリング手法。

ORMとの相性やアスタ使用問題、カラムに制約付けられないなど、
問題が発生しそう。

制約

シングルアプリなら制約は緩めを許容できるかもしれない。
そのAPしか使わないのだから。そのAPで担保できる。
マルチだと複数の概念の異なるAPからアクセスされるのだから、
DB側でデータの整合性を担保しなければならない。
権限によるアクセス制御とかも。

不吉なにおい(P.23)

削除前の退避

SET UNUSED

カラムを削除せずに非表示にする

CREATE TABLE XXX AS SELECT

計算カラム

ストレージとパフォーマンスのトレードオフ。
情報を最新に保つようにしなくてはならない。(トリガーやバッチなど)
イミュータブルならばいいけれども。
同期のタイミングをどうするか。

ビュー

ビュー自身にインデックスは貼れない。
元の表のインデックスが使われるかもしれない。
マテビューならインデックスを付与できる。
サマリーテーブルとの違いは、リフレッシュコマンドですぐ再構築できるくらい?

更新可能ビュー

テーブル名変更に使える。
古いテーブル名でビューを作っておく。

テーブル/カラムの同期

名称変更時に、これらのカラムを並行して保持する場合、同期する必要がある。
トリガーかビューかバッチ。

サロゲートキー

P.80

マイクロサービスはDBリファクタリングが容易では?

厳密なマイクロサービスであれば、アプリごとにDBを保持している。
つまりそのDBを扱うのはそのアプリしか存在しないのだから、
アプリとDBの整合性を容易に保てる。

テーブルの分割

外部キーによる参照制約

取りうる値をPKとしたテーブルを作成。
対象のカラムに上記のテーブルに対するFKを貼れば、ドメインが保証される。
カラム制約や列挙型ではなく、FKで制約を入れた。
コードの追加も楽。

ON UPDATE CURRENT_TIMESTAMP

トリガー

トリガー失敗時のロールバック。
トリガー循環問題。

論理削除

WHEREでフラグを見なければならないから面倒。
レコード数も増えてパフォーマンスが低下する。

後者において、削除データも含めてユニーク制約を持たせたい場合、
クラステーブル継承で親テーブルにその制約を持たせるか。
もしくは部分インデックスで、有効なデータのカラムにのみユニーク制約を持たせる。

PostgreSQLであれば継承が使えるが、複雑になるしORMもサポートしているか分からないので使用しない。
5.11. 継承

「スマート」カラム

桁に情報を持たせる奴。
NFに反するけど、状況によっては許容できるか。
ビットで情報を持たせたり。
レコードサイズも減るよね。

制約はパフォーマンスと一貫性のトレードオフ