データベースのスキーマとは?三層スキーマ(外部・概念・内部)の違いと設計・管理の実務
データベースの文脈でスキーマと言うとき、二つの異なるものが同じ名前で呼ばれています。ひとつはデータベース全体の構造を記述した設計図としてのスキーマ、もうひとつは CREATE SCHEMA で作るオブジェクトの入れ物としてのスキーマです。基本情報技術者試験に出る「外部スキーマ・概念スキーマ・内部スキーマ」は前者の話であり、MySQL のスキーマ一覧に並ぶ名前は後者の話です。この記事では両者を切り分けたうえで、三層スキーマの違い、RDBMS ごとに異なるスキーマの実体、設計と変更管理の実務までを扱います(心理学で言うスキーマ=認知の枠組みとは別の概念です)。
まとめ:スキーマの要点
- スキーマには「データベースの構造定義(設計図)」と「オブジェクトの名前空間(入れ物)」の二つの意味があり、文脈で使い分けられている。
- 三層スキーマは ANSI/SPARC が示した構造で、外部スキーマ=利用者ごとの見え方(ビュー)、概念スキーマ=業務データの論理モデル(表定義・正規化)、内部スキーマ=物理的な格納方法(ファイル編成・インデックス)に対応する。
- 三層に分ける目的は、上の層を壊さずに下の層を変えられること(論理的データ独立性・物理的データ独立性)に尽きる。
- 実装上のスキーマは RDBMS ごとに意味が違う。MySQL では
CREATE SCHEMAはCREATE DATABASEの同義語で、PostgreSQL ではデータベースの下位に位置する名前空間になる。 - スキーマ設計は概念設計(ER 図)→論理設計(正規化・表定義)→物理設計(インデックス・パーティション)の順に降りる。三層スキーマの各層とほぼ対応する。
- 運用フェーズのスキーマ管理は、マイグレーションファイルでの履歴管理と、追加してから削る二段階の変更(expand/contract)が中心になる。
以下、それぞれを実装レベルまで掘り下げます。
スキーマの二つの意味:構造定義と名前空間
スキーマ(schema)はギリシャ語由来の「形・枠組み」を語源とする語で、データベースでは「どんなデータをどう持つかを記述したもの」を広く指します。ただし現場では二つの層で使われており、この混線が「スキーマとは何か」を分かりにくくしています。
構造定義としてのスキーマ(設計図)
テーブル、カラムとその型、主キー・外部キー、制約、ビュー、インデックスといった要素の定義の集合が、構造定義としてのスキーマです。DDL(CREATE TABLE など)で記述され、データそのものではなく「データの入れ物の形」を決めます。試験や設計論で「スキーマ」と言えばほぼこの意味で、三層スキーマもこの層の議論です。
名前空間としてのスキーマ(実装上の入れ物)
SQL 標準では、スキーマはテーブルやビュー、関数などのオブジェクトをまとめる名前空間です。PostgreSQL で sales.orders と hr.orders が衝突せず共存できるのは、sales と hr という別スキーマにいるから。部門やテナント、本番と検証といった単位でオブジェクトを区切るための道具であり、設計図というより名前の階層と考えるほうが実態に近くなります。
スキーマ・データベース・テーブルの違い
階層で見ると分かりやすく、PostgreSQL や Oracle ではデータベース>スキーマ>テーブルの入れ子になります。テーブルは行と列でデータを実際に保持する実体、スキーマはそのテーブル群をまとめる区画、データベースはスキーマ群を含む最上位の器です。ただしこの階層は RDBMS によって崩れます(後述)。「スキーマとテーブルの違い」を問われたときは、スキーマは名前の区画であってデータを持たない、と答えるのが実態に合っています。
三層スキーマ(ANSI/SPARC):外部・概念・内部の違い
三層スキーマは、米国規格協会(ANSI)の下部組織 ANSI/X3/SPARC のデータベース研究グループが 1975 年の中間報告で提唱した構造モデルです。同じデータベースを、利用者から見た姿・業務としての姿・機械が格納する姿の三つのレベルで記述し分けます。基本情報技術者試験や応用情報技術者試験、データベーススペシャリスト試験で繰り返し問われる古典ですが、暗記用の語ではなく、現在のリレーショナルデータベースにそのまま実体があります。
| 層 | 記述する内容 | RDBでの実体 | 主な利用者 |
|---|---|---|---|
| 外部スキーマ | 利用者ごとの見え方 | ビュー、サブスキーマ | アプリケーション、業務利用者 |
| 概念スキーマ | 業務データの論理モデル | ER図、表定義、正規化 | 設計者、DBA |
| 内部スキーマ | 物理的な格納方法 | ファイル編成、インデックス | DBMS、DBA |
三層は上から下へ写像でつながっており、DBMS が層間の対応づけを担います。
外部スキーマ:利用者ごとの見え方
外部スキーマは、概念スキーマで定義されたデータのうち、その利用者に必要な部分だけを切り出した見え方です。リレーショナルデータベースではビューが該当します。たとえば給与テーブルに支給額と口座番号があっても、経理以外の部署には氏名と所属だけを見せるビューを与えれば、アプリケーションは基底テーブルの全体像を知る必要がありません。外部スキーマは一つのデータベースに対して複数存在します。
読み取り負荷が問題になる集計系のビューでは、結果を実体として保持するマテリアライズドビューを使う選択肢もありますが、これは外部スキーマの見え方に内部スキーマの都合(格納とリフレッシュ)が持ち込まれる形になります。層をまたぐぶん、リフレッシュ間隔が業務の許容できるデータ遅延を超えないかを先に決めてから採用します。
概念スキーマ:業務データの論理モデル
概念スキーマは、対象業務にどんなデータが存在し、それらがどう関係するかを、特定の利用者にも特定の格納方式にも依存しない形で記述したものです。エンティティと関連(リレーションシップ)を洗い出す ER 図、そこから導いた表定義、正規化による冗長の排除がこの層に当たります。複数存在しうるのは外部スキーマだけで、概念スキーマと内部スキーマはデータベース全体で一つです。
「概念スキーマ=概念設計の成果物」とは限らない点に注意が必要です。日本語の設計工程で言う概念設計は ER 図までを指すことが多く、正規化して表定義に落とす論理設計まで含めた全体が、三層スキーマで言う概念スキーマに相当します。
内部スキーマ:物理的な格納方法
内部スキーマは、概念スキーマで定義されたデータを記憶装置上にどう置くかの記述です。ファイル編成、ページサイズ、インデックスの種類と対象列、パーティションの切り方、圧縮の有無などが含まれます。PostgreSQL で全文検索や JSONB の検索を速くするために GIN インデックスを張る判断は、概念スキーマ(テーブル定義)を一切変えずに内部スキーマだけを変える典型例です。
三層に分ける目的:論理的データ独立性と物理的データ独立性
三層スキーマの価値は分類そのものではなく、層と層を切り離すことで得られる二つの独立性にあります。
物理的データ独立性は、内部スキーマを変えても概念スキーマとアプリケーションが影響を受けないことです。インデックスを追加しても、テーブルを別のディスクへ移しても、SELECT 文は書き換えずに済みます。論理的データ独立性は、概念スキーマを変えても外部スキーマ経由のアプリケーションが影響を受けないことです。テーブルを分割しても、分割前と同じ列を返すビューを維持すれば、そのビューを見ているアプリケーションは変更を知らずに動き続けます。
物理的独立性はほとんどの RDBMS で自動的に得られますが、論理的独立性は設計者がビューを介在させて初めて得られます。アプリケーションが基底テーブルを直接参照している設計は、外部スキーマの層を省いた設計であり、テーブル構造を変えた瞬間に全アプリが壊れます。三層スキーマが実務で効いてくるのは、ここです。
RDBMSごとに違う「スキーマ」の実体
三層スキーマの説明を読んだうえで SHOW SCHEMAS を叩くと、多くの人が混乱します。実装上のスキーマ(名前空間)は製品ごとに位置づけが異なり、同じ SQL が別の意味になるためです。
MySQL:データベースの別名としてのスキーマ
MySQL リファレンスマニュアル(13.1.12 CREATE DATABASE)は CREATE SCHEMA を CREATE DATABASE の同義語と定義しており、スキーマとデータベースは同じものを指します。したがって MySQL に「データベースの中に複数のスキーマを作る」という階層は存在せず、テーブルは データベース名.テーブル名 の二段で修飾されます。MySQL しか触っていない開発者が「スキーマ=データベース」と理解しているのは、その環境では正しい理解です。
CREATE SCHEMA sales; -- CREATE DATABASE sales; と同じ結果になる
SHOW SCHEMAS; -- SHOW DATABASES; と同じ一覧が返る
PostgreSQL:データベースの下位にある名前空間
PostgreSQL では、一つのデータベースが複数の名前付きスキーマを持ち、スキーマがテーブルを持ちます。ただし実際に書く修飾は スキーマ名.テーブル名 の2段です。データベース名.スキーマ名.テーブル名 も構文としては書けますが、公式ドキュメントが「SQL 標準への形式的な準拠のため」と明記しているとおり、指定できるのは接続中のデータベース名だけで、他のデータベースのテーブルは参照できません。スキーマ名を省略したときにどこを探すかは search_path が決め、既定値は "$user", public です。自分のユーザー名と同じスキーマがあればそこを、なければ public スキーマを見ます。
-- 現在の探索順を確認し、業務スキーマを優先させる
SHOW search_path; -- 既定: "$user", public
SET search_path TO sales, public;
SELECT * FROM orders; -- sales.orders が解決される
権限の既定値はバージョンで変わっています。PostgreSQL 14 以前は public スキーマに対して全ユーザーが CREATE 権限を持っており、接続できる誰もがテーブルを作れました。PostgreSQL 15 以降はこの権限が既定で外れています。ただし 14 以前から pg_upgrade でアップグレードしたデータベースには旧来の権限が残るため、PostgreSQL 公式ドキュメント(5.10 スキーマ)は次の明示的な剥奪を促しています。バージョンを上げただけで安全になったと考えるのは誤りです。
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
なお PostgreSQL と MySQL は、スキーマの扱い以外にもデータ型や全文検索、レプリケーションの設計が異なります。選定の観点は PostgreSQLとMySQLの違いの比較で整理しています。
Oracle・SQL Server:ユーザーと dbo
Oracle ではスキーマはユーザーと一対一で結びついており、ユーザーを作ると同名のスキーマができ、そのユーザーが作ったオブジェクトはそのスキーマに属します。スキーマを分ける=アカウントを分けることになります。SQL Server はスキーマとユーザーが分離しており、既定のスキーマは dbo です。テーブルを dbo.Orders と書くのはこのためで、業務ごとに Sales.Orders のようにスキーマを切って権限を付与するのが本来の使い方です。
マルチテナントの設計でスキーマ分離を選ぶ場合、この差はそのまま移植性の壁になります。PostgreSQL でテナントごとにスキーマを切る設計は自然ですが、同じ設計を MySQL に持ち込むとテナントごとにデータベースを作ることになり、接続とバックアップの単位が変わります。「スキーマで分ける」という設計判断は、RDBMS を決めてからでないと意味が定まりません。
データベースのスキーマ設計の進め方:概念設計・論理設計・物理設計
スキーマ設計は、業務の言葉から始めて機械の都合へ降りていく工程です。三層スキーマの各層とほぼ対応しており、上流の判断ミスは下流で取り返せません。
概念設計:エンティティと関連を洗い出す
業務で扱う対象(顧客、注文、商品)をエンティティとして抽出し、それらの関連と多重度(1対多、多対多)を ER 図で表します。記法は IE 記法(鳥の足)か IDEF1X のどちらかに統一しておくと、多重度の読み違いが起きません。この段階では特定の RDBMS も性能も考えません。多対多の関連を見つけたら、後段で中間テーブルに落ちることを見越して関連名と属性を確定させておきます。ここで業務ヒアリングを省くと、実装後に「その属性は履歴を持つ必要があった」という形で破綻します。
論理設計:正規化して表定義に落とす
ER 図をテーブル・カラム・型・主キー・外部キーに変換し、正規化で更新異常(挿入時・更新時・削除時の3種の矛盾)を排除します。実務では第三正規形までを基準に置き、参照が集中する箇所だけ意図的に非正規化を残す形が現実的です。重要なのは、非正規化を「性能のため」と曖昧に決めないこと。どのクエリがどれだけ遅く、非正規化でどれだけ改善するのかを測ってから決めます。測らずに非正規化した設計には、整合性の担保コストだけが残ります。
物理設計:インデックスと格納方式を決める
実際に流れるクエリを前提に、インデックス、パーティション、ストレージパラメータを決めます。インデックスは検索を速くする代わりに更新を遅くし、容量も食う。だから「主キー以外は、遅いクエリの実行計画を見てから張る」という順序を守ります。PostgreSQL なら EXPLAIN (ANALYZE, BUFFERS) で実測の行数とバッファ読み取り量を確認し、Seq Scan が問題の規模かを判断してから設計に反映します。想定で先回りして張ったインデックスの多くは使われず、書き込みの負担だけが残ります。
スキーマ管理:変更履歴とダウンタイムなしの変更
設計より難しいのは、稼働中のデータベースのスキーマを変え続けることです。テーブルにデータが入り、アプリケーションが動いている状態での ALTER TABLE は、設計変更ではなく無停止の外科手術になります。
マイグレーションでスキーマの変更履歴を管理する
スキーマ変更は SQL を手で流すのではなく、マイグレーションファイルとしてコードと同じリポジトリで管理します。Python なら SQLAlchemy 向けの Alembic、Ruby on Rails なら Active Record、Java なら Flyway や Liquibase が定番です。ファイルが順序付きで積み上がることで、どの環境がどの版のスキーマかをリビジョンで特定でき、検証環境と本番の差分がなくなります。
実務では、ロールバック用の down スクリプトを書いても本番で使う機会はほとんどありません。データが入ってしまった後にカラムを消す down を流せばデータが消えるからです。壊れた変更は前の版へ戻すのではなく、打ち消す新しい変更を前へ積む(前進のみのマイグレーション)ほうが安全です。
expand/contract:追加してから削る二段階の変更
アプリケーションを止めずにカラム名を変える、という要求は単独の ALTER TABLE では満たせません。新旧どちらのアプリケーションも同時に動く瞬間があるためです。そこで変更を二段階に割ります。まず新しいカラムを追加して両方に書き込むようにし(expand)、読み取りを新カラムへ切り替え、旧カラムを参照するコードが完全に消えたことを確認してから旧カラムを削除します(contract)。
この手順は、スキーマ変更をアプリケーションのデプロイと同時にやらないという原則から導かれます。スキーマの変更とコードの変更を同じリリースに束ねると、どちらかのロールバックがもう一方を壊します。両者を分け、常に「旧コードでも新スキーマで動く」状態を保つのが、無停止運用の実際です。
よくある質問
スキーマとテーブルの違いは何ですか?
テーブルは行と列でデータを実際に保持する実体で、スキーマはそのテーブルなどのオブジェクトをまとめる名前空間、または構造全体の定義です。スキーマ自体はデータを持ちません。PostgreSQL では スキーマ名.テーブル名 の関係になり、MySQL ではスキーマがデータベースと同義のため データベース名.テーブル名 になります。
外部スキーマはビューのことですか?
リレーショナルデータベースでは、外部スキーマの実体はビュー(およびアプリケーションに見せる部分集合の定義)です。ANSI/SPARC の三層スキーマはリレーショナルモデルに限定しない一般モデルのため「外部スキーマ」という抽象名になっていますが、RDBMS 上ではビューと読み替えて差し支えありません。
概念スキーマと概念設計は同じものですか?
厳密には違います。三層スキーマの概念スキーマは、ER 図に加えて正規化された表定義まで含む論理モデル全体を指します。一方、設計工程で言う概念設計は ER 図の作成までを指し、正規化と表定義は論理設計の担当とされるのが一般的です。概念スキーマ ≒ 概念設計 + 論理設計、と対応づけると混乱しません。
MySQL のスキーマとデータベースは違うものですか?
MySQL では同じものです。公式リファレンスマニュアルが CREATE SCHEMA を CREATE DATABASE の同義語と定義しており、GUI ツールが「スキーマ」と表示している対象はデータベースそのものです。PostgreSQL や Oracle の感覚でデータベースの下にスキーマを作ろうとすると、実際にはデータベースが増えます。
三層スキーマは誰が決めたものですか?
米国規格協会(ANSI)の下部組織である ANSI/X3/SPARC のデータベース研究グループが、1975 年の中間報告で提唱した構造モデルです。国際規格として強制力を持つ仕様ではありませんが、外部・概念・内部の三層という整理は各 RDBMS の設計思想に共通して残っており、日本の情報処理技術者試験でも定番の出題範囲になっています。