はじめに
開発をしていく上で、SQLの非効率な書き方やDB設計のアンチパターンを知っておきたいと思いました。
著書「SQLアンチパターン」を読んで、各部から印象に残った章の内容を抜粋してまとめていきます。
読みづらいところなどあるかと思いますが、少しでも参考になれば幸いです。
SQLアンチパターンの要約一覧です。
他の章についても要約してます!
Ⅳ部 アプリケーション開発のアンチパターン
23章 ディプロマティック・イミュニティ(外交特権)
アンチパターン:SQLを特別扱いする
「アプリケーション開発のルールはデータベース開発には当てはまらない」
このような考えを著者は「ディプロマティック・イミュニティ(外交特権)」アンチパターンと名付けています。
そう考えられる理由を抜粋して以下に挙げます。
- リレーショナルデータベースで用いられるSQL言語は、従来型のプログラミング言語とは性質が異なります。SQLステートメントがアプリケーションコード内で特殊な言語として扱われる様子も、データベース管理者が「お客さん」のように扱われる状況を象徴しているかのようです
上記を言い換えてみますと、
SQLは、一般的なプログラミング言語(Java、Pythonなど)とは異なり、データの検索や更新といった特定の目的に特化しています。
多くのアプリケーション開発者は、ORMなどの仕組みによってより簡単にデータベースにアクセスできるため、SQLについてそこまで詳しくなくてもデータベースを利用することができます。
そのため、データベース管理者は、アプリケーション開発者からすれば「SQLの専門家」という位置づけになり、まるで「お客さん」のように扱われることがあるということです。
アンチパターンを用いても良い場合
APIの使い方を思い出すための1回限りのテストや、ユーザーの質問に答えるためのSQLクエリなど、その場限りのコードを書くときなどです。
解決策:包括的に品質問題に取り組む
ソフトウェア開発者の多くにとって、品質とは単にテストを行うことを意味します。しかし、これは品質管理(Quality Control: QC)にすぎません。
ソフトウェアエンジニアリングのライフサイクルは、品質保証(Quality Assurance: QA)を伴います。品質保証は3つの部分から成り立ちます。
1. プロジェクト要件の明確な定義・文書化
2. 要件に対する解決策の設計・構築
3. 解決策が要件を満たしていることの確認・テスト
データーベース開発における品質保証は、文書化、バージョン管理、テスティングのベストプラクティスに従うことで達成できます。
つまり、
品質管理(QC)と品質保証(QA)の違いについては、こちらの記事もご参考ください。
品質管理(QC)とは?品質保証(QA)との違いや役立つ手法も解説
①文書化
データベースの要件と実装は、アプリケーションコードと同じように文書化すべきです。
文書化する方法を抜粋して以下に挙げます。
ER図
テーブルとその関連(リレーションシップ)を表す図です。
より詳細なER図には、列、キー、インデックスなどのデータベースオブジェクトを記すための表現法があります。
注意しなければいけないのは、非常に多くのテーブルを持つ複雑なデータベースを、1つの図で表すのは現実的でないことです。このような場合は、複数のER図に分割します。
テーブル、列、ビュー
ER図は、テーブルや列などのオブジェクトの目的や利用方法の記述には適していないため、データベースを説明する文書も必要です。
テーブルには、テーブルが表現しているエンティティの分類についての説明が必要です。例えば、Bugs、Products、Accountsなどは、名前からそのエンティティを特定しやすいと言えます。しかし、BugStatusのような参照テーブルや、BugsProductsのような交差テーブル、Commentsテーブルのような従属テーブルには説明が必要です。
各テーブルで想定している行数や、テーブルに対して実行されるクエリ、テーブルに構築するインデックスについても記述します。
関連(リレーションシップ)
参照整合性制約はテーブル間の依存関係を実現しますが、制約のモデリングについての開発者の意図を全て表しているとは限りません。
例えば、Bugs.reported_by
はNULLを許容する列ではありませんが、Bugs.assigned_to
はNULLを許容します。これは、バグが割り当てられる前に、修正可能であることを意味しているのでしょうか?
明示的に制約が定義されていないものの、暗黙的な関連がある場合もあるため、ドキュメントを残すことが大切です。
②バージョン管理
私たちは、バージョン管理システムを用いて、アプリケーションコードの管理や、複雑な履歴管理の問題を解決する方法を知っています。データベースを扱うコードに対してもバージョン管理を用いることで、開発において同様のメリットを得られます。
以下はデータベース開発関連のファイルの一部です。
データ定義スクリプト
CREATE TABLE
などのステートメントでデータベースオブジェクトを定義するSQLスクリプト群です。
ブートストラップデータ
ユーザーが使い始める前に、データの初期状態として参照テーブルにデータを格納しておくことがあります。プロジェクトのソースからデータベースを復元する場合は、このようなブートストラップデータを管理します。シードデータ(seed data)とも呼ばれます。
ER図とドキュメント
これらはコードではありませんが、コードと密接に結びついており、データベースの要件、実装、アプリケーションとの統合について記述されています。データベースとアプリケーションの両方に変更が必要になった場合は、これらのファイルを最新にしておくべきです。
スキーマ進化ツール
Ruby on Railsは、バージョン管理下のデータベースインスタンスに対する変更を管理するマイグレーションと呼ばれる手法を普及させました。
データベースの変更を行うためのRailsの抽象クラス(ActiveRecord::Migration)を継承して、データベース定義をワンステップずつ変更するコード(upメソッド)を作成します。また、変更をも元に戻す機能(downメソッド)も記述します。
class AddHoursToBugs < ActiveRecord::Migration
def self.up
add_column :bugs, :hours, :decimal
end
def self.down
remove_column :bugs, :hours
end
end
③テスティング
品質保証の最終パートは、品質管理(QC)です。すなわち、アプリケーションが設計通りに動作することの検証です。
テスティングの重要な原則の1つは、独立(isolation)です。テストごとにシステムの1部分のみを検証することで、欠陥が存在する場合に、可能な限り正確に、欠陥の箇所を絞り込めます。
以下はデータベースの妥当性を検証するテストのためのチェックリストの一部です。
テーブル、列、ビュー
データベースに存在すべきテーブルやビューが、実際に存在することをテストで確認します。
また、否定テスト(negative test)を作成して、プロジェクトの現在のリビジョン(ソフトウェアやハードウェアに対する細かな修正)から削除したテーブルや列が、本当に存在していないことを確認できます。
制約
制約に対しても、否定テストができます。制約に違反してエラーになるはずのINSERT、UPDATE、DELETEステートメントをテストで実行して、実際にエラーが生じることを確認します。例えば、NOT NULL制約、一意性制約、外部キー制約などに違反するようなステートメントを実行します。
エラーを返さなければ、制約は機能していないことになるため、障害を初期段階で特定できます。
ブートストラップデータ
空の状態であることが想定されるデータベースでも、通常は参照テーブルなどに初期データが必要です。初期データの存在を確認するためのクエリを実行してテストします。
クエリ
アプリケーションコードは、SQLクエリと組み合わせて使用されます。構文と結果の確認のために、テスト環境でクエリを実行できます。テーブルやビューのテストと同じように、結果セットに適切な列名とデータ型が含まれていることを確認します。
まとめ
SQLを他のプログラミング言語とは異なる特別な存在として扱い、品質管理を怠ってしまうことはアンチパターンの一つである。
このような包括的な問題を改善するために、文書化、バージョン管理、テスティングのような品質保証に取り組むことが重要。
所感
今回は時間の都合で抜粋してまとめました。ですが正直言って、他の章の内容も良かったので、テーマを一つに絞って書くのは難しかったです。
ただそれよりも重要なのは、「実務や実践の場で活かす」ことだと思っています。今後DB設計や実装をしていく中で、アンチパターンについて思い出しながら理解を深めていければと思います。
参考記事
品質管理(QC)とは?品質保証(QA)との違いや役立つ手法も解説
ソフトウェア・テストにおけるネガティブ・テスト – ネガティブ・テストとは何か、その種類、プロセス、アプローチ、ツール、その他!