結論

データベーススペシャリスト試験は、IPAが行うデータベース設計・運用の国家試験(レベル4)です。2026年度は2027年2〜3月にCBT方式で行われます。

この記事でわかること

  • 2026年度の日程と4科目の時間・合格基準の表
  • 合格率4回分と、午後10問の題材・講評の傾向
  • 正規化・SQL・障害回復の実行例25本と出力

2026年10月22日更新|IPA公式で確認・PostgreSQL 16.15で実行

試験は4つの科目に分かれ、科目Aの2つと科目Bの2つを別の日に受けます。どの科目も60点に届かないと、その先の科目は採点されません。

flowchart TD
    A[申込み<br>2027年1月27日から] --> B[科目A-1 50分<br>科目A-2 40分]
    B -->|両方60点以上| C[別の日に<br>科目B-1 90分]
    B -->|どちらか60点未満| F[科目Bは採点されず<br>不合格]
    C -->|60点以上| D[科目B-2 120分]
    D -->|60点以上| E[合格<br>発表は2027年6月頃]

急いでいる人向けに、要点を先にまとめます。

項目 内容 補足
次の試験 2026年度後期(2027年2〜3月) CBT方式。前期には行われない
申込み 2027年1月27日〜2月27日 科目Aと科目Bを同時に予約
試験時間 A-1 50分・A-2 40分・B-1 90分・B-2 120分 科目Aと科目Bは別の日
合格基準 4科目とも100点満点中60点以上 足切りあり(上の図)
合格率 18.4%(令和7年度秋期) 受験者9,769人・合格者1,796人
受験手数料 7,500円(消費税込み) 2026年10月22日時点
免除 科目A-1(応用情報の合格者など) 条件を満たしてから2年間
午後の中心 概念データモデル・正規化・SQL・同時実行制御 記述式。B-1は3問中2問

確認環境:Ubuntu 24.04・PostgreSQL 16.15(psql 16.15)・Python 3.11.15。正規化・関数従属性の確かめ方・スーパータイプとサブタイプ・SQL・分離レベル・デッドロック・ロック待ち・2相コミット・異常終了からの回復・索引のクラスタ性を、自作の小問で実際に動かして出力を載せています(過去問の転載はしていません)。試験の形式・時間・配点・合格基準・免除は IPA の試験要綱 Ver.5.6、日程と手数料は IPA の令和8年度試験の日程ページ、CBT 方式は IPA の CBT 方式のページ、出題範囲はデータベーススペシャリスト試験シラバス Ver.4.1、合格率は IPA の合格発表のお知らせ、午後の題材は IPA の令和6・7年度の採点講評、新制度は IPA のプレスリリースで、PostgreSQL の仕様は PostgreSQL 16 の公式ドキュメントで確かめました(確認日 2026年10月22日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「データベーススペシャリスト試験とは|日程・合格率・午後対策を実例で解説」

データベーススペシャリスト試験とは

この節では、どんな試験で、誰に向けたものかが分かります。

データベーススペシャリスト試験(略号 DB、英語名 Database Specialist Examination)は、IPA(独立行政法人 情報処理推進機構)が行う情報処理技術者試験の高度試験の1つです。 IPA の区分ページは、データベースを管理し、データ分析の基盤を提供する管理者やインフラ系のエンジニアを目指す人に向く試験と説明しています。

試験要綱の「対象者像」は、データベースの固有技術を使って、情報システム基盤の企画から保守までの中心的な役割を担い、技術支援を行う人です。期待する技術水準として、次のことが挙げられています。

1技術を選ぶ

データベース技術の動向をつかみ、目的に合った技術を選べる。

2データ資源を管理する

データ資源管理の技法を理解し、データ項目の標準化やリポジトリを扱える。

3データモデルを作る

データモデリングの技法で、正確な概念データモデルを作れる。

4品質の高いDBを作る

DBMS の特性を理解し、情報セキュリティも考えたデータベースを作れる。

レベルは、共通キャリア・スキルフレームワークの「レベル4」です。試験は応用情報技術者試験(レベル3)の1つ上に位置します。年齢・学歴・実務経験による受験の条件は、試験要綱には定められていません。

試験の名前が変わった点(午前Ⅰ・午後Ⅱ → 科目A-1・科目B-2)

2026年度から、科目の呼び方が変わりました。 出題範囲・出題形式・出題数・試験時間は変えない予定、と IPA は案内しています。過去問や参考書は前の呼び方なので、対応を押さえておくと迷いません。

前の呼び方 2026年度からの呼び方 中身
午前Ⅰ 科目A-1 高度試験に共通の多肢選択式
午前Ⅱ 科目A-2 データベース中心の多肢選択式
午後Ⅰ 科目B-1 記述式(3問から2問)
午後Ⅱ 科目B-2 記述式(2問から1問)

この記事では、過去の試験の話をするときは「午後Ⅰ」など前の呼び方を、2026年度の試験の話をするときは「科目B-1」などの新しい呼び方を使います。

2026年度の試験日程(CBT方式・2027年2〜3月)

この節では、次に受けられる試験の申込み期間と試験期間が分かります。

データベーススペシャリスト試験は、2026年度は「後期」に CBT 方式で行われます。 前期(2026年10〜11月)には行われません。令和4〜7年度は、いずれも10月に紙の試験で行われていました(令和7年度は2025年10月12日)。

予定 期間(2026年10月22日時点の IPA の案内)
申込み受付 2027年1月27日〜2月27日
科目A-1・科目A-2 2027年2月20日〜3月2日
科目B-1・科目B-2 2027年3月16日〜3月28日
合格発表 2027年6月頃(予定)

CBT(Computer Based Testing)は、テストセンターのパソコンで受ける方式です。IPA の CBT 方式のページには、次のように書かれています(いずれも予定)。

  • 試験会場は全国にあり、実施期間中に空席のある会場・日時から、都合に合わせて選んで申し込む。
  • 「科目A群」と「科目B群」は実施期間を分け、申込みのときに両方を同時に予約する。
  • 科目A-1と科目A-2の間、科目B-1と科目B-2の間に、10分の休憩を設ける。
  • 画面で問題を読み、キーボードとマウスで解答する。
  • 体の不自由などで CBT で受けられない人には、筆記による特別措置の試験を行う。

試験の形式・時間・配点・合格基準

この節では、4つの科目それぞれの時間・問題数・配点と、合格の条件が分かります。

科目 時間 形式・問題数 配点 基準点
科目A-1(午前Ⅰ) 50分 四肢択一 30問すべて解答 1問3.4点(上限100点) 60点
科目A-2(午前Ⅱ) 40分 四肢択一 25問すべて解答 1問4点 60点
科目B-1(午後Ⅰ) 90分 記述式 3問から2問 1問50点 60点
科目B-2(午後Ⅱ) 120分 記述式 2問から1問 1問100点 60点

試験要綱に書かれている合格の決まりは次のとおりです。

  • 4科目すべてが基準点(60点)以上で合格です。合計点では決まりません。
  • 採点は素点方式です(IRT は使いません)。試験の結果に難易差があったときは、基準点を変えることがあります。
  • 科目A-1か科目A-2のどちらかが60点未満なら、科目B-1・B-2は採点されません。 科目B-1が60点未満なら、科目B-2は採点されません(多段階選抜)。
300分4科目の試験時間の合計科目Aと科目Bは別の日
60点4科目それぞれの基準点1科目でも届かないと不合格
3問記述式で解く問題の数B-1で2問、B-2で1問

受験料・受験資格・科目A-1(午前Ⅰ)の免除

この節では、受験にかかるお金と、科目A-1を免除できる条件が分かります。

受験手数料は7,500円(消費税込み)です(2026年10月22日時点)。 年齢・学歴・実務経験による受験の条件は、試験要綱には定められていません。

科目A-1は、次のどれかを満たすと、その後2年間、受験を免除されます(試験要綱の「免除制度」)。

条件 申込みのときに入れる番号
応用情報技術者試験に合格した 合格証書番号
いずれかの高度試験か、情報処理安全確保支援士試験に合格した 合格証書番号
高度試験・支援士試験の科目A-1(令和7年度までは午前Ⅰ)で基準点以上をとった 午前Ⅰ通過者番号

IPA の免除のページによると、申込みのときに「一部免除申請番号」を入れないと、条件を満たしていても免除になりません。番号は、合格発表の後に受験者マイページで確かめられます。科目A-2に、データベーススペシャリスト試験を受ける人向けの免除はありません。

合格率と受験者数の推移

この節では、IPA が発表した直近4回の合格率と人数が分かります。

試験 応募者数 受験者数 合格者数 合格率
令和7年度秋期(2025年10月12日) 14,416人 9,769人 1,796人 18.4%
令和6年度秋期(2024年10月13日) 14,549人 10,120人 1,744人 17.2%
令和5年度秋期(2023年10月8日) 13,121人 8,980人 1,664人 18.5%
令和4年度秋期(2022年10月9日) 12,399人 8,445人 1,486人 17.6%

合格率は4回とも17〜19%の間です。 合格率は「合格者数÷受験者数」で、応募しても受けなかった人は数に入りません。令和7年度は、応募者14,416人のうち受験したのは9,769人(約68%)でした。合格者の平均年齢は、令和7年度が31.3歳、令和6年度が30.9歳、令和5年度が30.8歳です。

18.4%令和7年度秋期の合格率合格者1,796人
9,769人令和7年度秋期の受験者数応募者は14,416人
31.3歳令和7年度の合格者の平均年齢IPA の発表

2027年度からの新しい試験制度

この節では、データベーススペシャリスト試験が今後どうなる予定かが分かります。

IPA の区分ページには「現行の試験制度は2026年度の実施をもって終了し、2027年度から新制度へ移行する予定」とあります。 2026年3月31日の IPA のプレスリリースでは、応用情報技術者試験と高度試験を「大括り化」して再編し、「プロフェッショナルデジタルスキル試験(仮称)」とする案が示されています。

予定(2026年10月22日時点・いずれも仮称を含む案) 内容
プロフェッショナルデジタルスキル試験(仮称) マネジメント・データ・AI・システムの3領域の試験区分を設ける
データマネジメント試験(仮称) IT パスポート試験の次のステップとして新設
開始の時期 2027年度の夏頃から秋頃
現行制度での免除の扱い 現行制度で免除の条件を満たした人は、一定期間、一部科目の免除を検討中

プレスリリースには「データベーススペシャリスト試験」という名前での個別の扱いは書かれていません。シラバス案のページ(最終更新 2026年9月30日)でも、データ・AI 領域のシラバス案は「準備中」です。現行のデータベーススペシャリスト試験として受けられるのは、2027年2〜3月の後期試験が最後になる予定です。

出題範囲(科目A-2と科目Bの分野)

この節では、どの科目で何が出るかが分かります。

科目A-1・科目A-2(午前)

科目A-1は高度試験に共通の問題で、応用情報技術者試験のレベル(レベル3)の範囲から出ます。科目A-2は、データベース・セキュリティ・コンピュータ構成要素・システム構成要素・システム開発技術・ソフトウェア開発管理技術の6分野から出題され、中心はデータベースです。

科目B-1・科目B-2(午後)

科目Bは記述式で、次の3つの分野から出ます。

分野 主な内容
データベースシステムの企画・要件定義・開発 概念データモデル・関係スキーマ・正規化・物理設計・データ移行
データベースシステムの運用・保守 性能の見積りとチューニング・障害回復・再構成
データベース技術 SQL・トランザクション・同時実行制御・索引・DBMS の機能

シラバス Ver.4.1 は、この中身を5つの大項目で細かく書いています。

大項目 中項目(シラバス Ver.4.1)
1 データベースの全体計画 全社データベースの計画・データ定義の標準化
2 データベースの要件定義 現状調査と課題分析・作業範囲の確定・初期要件
3 データベースの分析・設計 概念データモデルの作成と検証・論理データモデルの作成と検証
4 データベースの実装・テスト RDBMS の選定・物理設計(ロック方式・索引・性能見積り・レプリケーション)・実装・移行
5 データベースシステムの運用・管理 運用・保守計画・インテグリティの定期確認・性能チューニング・利用者サポート

午後(科目B)の出題傾向:令和6・7年度の10問

この節では、最近の午後の問題が何を題材に、何を問うたかが分かります。

IPA は、午後Ⅰ・午後Ⅱの問題ごとに「採点講評」を公開しています。令和6・7年度の採点講評から、題材と問われた技術、正答率が低かった点をまとめました(問題文は転載していません)。

試験 題材 問われた技術 正答率が低かった点(講評の要約)
R7 午後Ⅰ 問1 大学の学習管理システム 概念データモデル・サブタイプ 切り口が3つあるサブタイプ構造
R7 午後Ⅰ 問2 化学メーカーの営業販売 正規化・関係スキーマ 第3正規形でない根拠に沿った分解、主キーの漏れ
R7 午後Ⅰ 問3 じゅう器メーカーの在庫管理 SQL・同時実行制御・索引 平均を求める集計関数、時刻印による制御
R7 午後Ⅱ 問1 コード決済システム 分離レベル・2相コミット ノンリピータブルリードとファントム、2相コミットの流れ
R7 午後Ⅱ 問2 宅配ピザの資材配送 概念データモデル 在庫の持ち方で決まるリレーションシップ
R6 午後Ⅰ 問1 オンライン学習の基盤 概念データモデル・主キーと外部キー リレーションシップの引き先、下線の誤り
R6 午後Ⅰ 問2 労務管理システム ロック・デッドロック 必要なロックの種類と対象表、ロック時間
R6 午後Ⅰ 問3 プロジェクト稼働管理 物理設計・SQL・性能 RESTRICT の動き、ウィンドウ関数、索引のクラスタ性
R6 午後Ⅱ 問1 オートリースの車両保守 障害回復・トランザクション設計 再始動の処理、ロールフォワードのページの扱い
R6 午後Ⅱ 問2 車体部品メーカーの資材調達 概念データモデル・関係スキーマ サブタイプのリレーションシップ

10問のうち5問が概念データモデル・正規化、5問が SQL・同時実行制御・障害回復・物理設計でした。 講評で繰り返し出てくる助言は「状況記述や帳票の例から、必要な属性と主キーを正確につかむ」「一般論ではなく、本文の事例に即して答える」の2つです。

講評で問われた技術は、この記事の後半で PostgreSQL 16 を使って1つずつ動かしています。

講評で問われた技術 この記事で動かした節
正規化・第3正規形でない根拠・主キー 正規化を PostgreSQL で確かめる
サブタイプ・リレーションシップ 概念データモデルと関係スキーマ
集計関数・ウィンドウ関数 午後で問われる SQL
分離レベル・ロック・デッドロック・時刻印 トランザクションと同時実行制御
2相コミット・再始動・ロールフォワード 障害回復と2相コミット
RESTRICT・索引のクラスタ性 参照動作・物理設計と性能

学習の順番と時間の目安

この節では、何から手を付け、時間をどう配分するかが分かります。

IPA は、合格に必要な学習時間の目安を公表していません。 必要な時間は、応用情報技術者試験の範囲がどれだけ身についているか(科目A-1の免除があるか)と、SQL・設計の業務経験で大きく変わります。午後の講評の傾向から、次の順番で進めると、科目Bの得点源(データモデルと正規化)を早く固められます。

1

関係モデルと正規化

関数従属性・候補キー・第1〜第3正規形・BCNF を、自分で表を分解して確かめます。午後のどの問題でも土台になります。

最初の土台

2

概念データモデル

エンティティ・リレーションシップ・スーパータイプとサブタイプ・連関エンティティを、状況記述から描く練習をします。

午後で最も多い題材

3

SQL

集約・相関サブクエリ・外部結合・ウィンドウ関数・CASE を、手元の DB で動かして結果を確かめます。

手で書いて動かす

4

トランザクションと障害回復

分離レベルと起きる現象、ロック・デッドロック、ログによるロールフォワード・ロールバック、2相コミットを押さえます。

午後Ⅱで深く問われる

5

物理設計と性能

索引の選び方・クラスタ性・ページ数と容量の見積り・表の区分化を、数値で計算できるようにします。

計算問題に備える

6

過去問を時間を計って解く

IPA が公開している問題冊子・解答例・採点講評で、午後Ⅰは1問45分、午後Ⅱは1問120分で解き、講評と照らします。

講評まで読む

時間の配分は、科目ごとの基準点と問題の量から考えると決めやすくなります。次の表は、合計150時間を使う場合の配分の例です(あくまで計画を立てるための例です)。

学習の段階 配分の例 使う教材
正規化・関係モデル 20時間 参考書・手元の DB
概念データモデル 30時間 過去問の午後Ⅰ・午後Ⅱ
SQL 20時間 手元の DB(PostgreSQL・MySQL など)
トランザクション・障害回復・物理設計 25時間 参考書・DBMS のドキュメント
科目A-1・A-2 の過去問 15時間 IPA の過去問(解答例つき)
午後の過去問(時間を計る) 40時間 IPA の過去問と採点講評

科目A-1の免除がない人は、応用情報技術者試験の範囲の復習を足します。記述式の過去問の進め方は応用情報の午後と共通する点が多く、関連記事「応用情報の午後(科目B)対策|過去問の使い方・選び方・答え方」で扱っています。IPA の過去問題のページには、問題冊子・解答例・採点講評が載っていて、利用に許諾や使用料は要らないと書かれています。

正規化をPostgreSQLで確かめる(関数従属性・候補キー・BCNF)

この節では、正規化の考え方を、表を実際に作って崩れ方と直り方で確かめられます。

正規化していない表で起きる異状

受注と明細と顧客と商品を1つの表にまとめた「受注明細_非正規」を作ります(自作の例です)。

CREATE TABLE 受注明細_非正規 (
  受注番号 int, 明細番号 int, 受注日 date,
  顧客コード text, 顧客名 text,
  商品コード text, 商品名 text, 単価 int, 数量 int,
  PRIMARY KEY (受注番号, 明細番号)
);
INSERT INTO 受注明細_非正規 VALUES
 (1001,1,'2026-10-01','C01','青葉商事','P10','ノートPC',120000,2),
 (1001,2,'2026-10-01','C01','青葉商事','P20','モニター',30000,3),
 (1002,1,'2026-10-02','C02','桜電機','P10','ノートPC',120000,1),
 (1003,1,'2026-10-05','C01','青葉商事','P30','キーボード',8000,5);

顧客名を変えるつもりで、1行だけを更新してしまうと、同じ顧客コードに2つの名前がある状態になりました(更新時異状)。

=> UPDATE 受注明細_非正規 SET 顧客名='青葉ホールディングス' WHERE 受注番号=1003;
UPDATE 1
=> SELECT DISTINCT 顧客コード, 顧客名 FROM 受注明細_非正規 ORDER BY 1,2;
 顧客コード |        顧客名        
------------+----------------------
 C01        | 青葉ホールディングス
 C01        | 青葉商事
 C02        | 桜電機
(3 rows)

まだ注文の無い新商品は、主キー(受注番号・明細番号)が空になるので登録できません(挿入時異状)。

=> INSERT INTO 受注明細_非正規 (商品コード, 商品名, 単価) VALUES ('P40','マウス',3000);
ERROR:  null value in column "受注番号" of relation "受注明細_非正規" violates not-null constraint
DETAIL:  Failing row contains (null, null, null, null, null, P40, マウス, 3000, null).

逆に、商品 P30 の唯一の注文(1003)を消すと、キーボードという商品があったこと自体が消えます(削除時異状)。

関数従属性をSQLで確かめる

関数従属性 X→Y は「X の値が決まると Y の値が1つに決まる」ことです。 「同じ X なのに Y が2種類以上ある行」が1つでもあれば成り立ちません。これは GROUP BY X HAVING count(DISTINCT Y) > 1 で探せます。

SELECT '受注番号→顧客コード' AS 従属性, count(*) AS 反例 FROM (SELECT 受注番号 FROM 受注明細_非正規 GROUP BY 受注番号 HAVING count(DISTINCT 顧客コード)>1) s
UNION ALL SELECT '顧客コード→顧客名', count(*) FROM (SELECT 顧客コード FROM 受注明細_非正規 GROUP BY 顧客コード HAVING count(DISTINCT 顧客名)>1) s
UNION ALL SELECT '商品コード→単価', count(*) FROM (SELECT 商品コード FROM 受注明細_非正規 GROUP BY 商品コード HAVING count(DISTINCT 単価)>1) s
UNION ALL SELECT '顧客コード→商品コード', count(*) FROM (SELECT 顧客コード FROM 受注明細_非正規 GROUP BY 顧客コード HAVING count(DISTINCT 商品コード)>1) s;
        従属性         | 反例 
-----------------------+------
 受注番号→顧客コード   |    0
 顧客コード→顧客名     |    0
 商品コード→単価       |    0
 顧客コード→商品コード |    1
(4 rows)

反例が0の3つは、今のデータでは成り立っています。「顧客コード→商品コード」は、C01 が P10・P20・P30 を買っているので成り立ちません。ただし、データで反例が0でも、関数従属性があるとは言い切れません。 関数従属性は業務の決まりで決まります。試験でも、状況記述(「顧客コードで顧客を一意に識別する」など)から読み取るのが基本で、SQL はその読み取りを手元のデータで確かめる道具です。

候補キーと正規形を判定する(Python)

関数従属性から候補キーと正規形を求める手順は、属性の「閉包」を使うと機械的にできます。閉包 X+ は、X から関数従属性をたどって決まる属性の全部です。 X+ がすべての属性になれば X はスーパーキーで、そこから1つも削れないものが候補キーです。手順を Python で書きました(与えた関数従属性だけを調べる、学習用の小さな版です)。

from itertools import combinations

def closure(attrs, fds):
    """属性集合 attrs の閉包 attrs+ を求める"""
    c = set(attrs)
    changed = True
    while changed:
        changed = False
        for lhs, rhs in fds:
            if lhs <= c and not rhs <= c:
                c |= rhs
                changed = True
    return c

def candidate_keys(R, fds):
    keys = []
    for n in range(1, len(R) + 1):
        for combo in combinations(sorted(R), n):
            s = set(combo)
            if any(k <= s for k in keys):
                continue          # 既に見つけたキーを含む=極小でない
            if closure(s, fds) == R:
                keys.append(s)
    return keys

def normal_form(R, fds):
    keys = candidate_keys(R, fds)
    prime = set().union(*keys)    # どれかの候補キーに含まれる属性
    level, why = 4, []            # 4=BCNF 3=第3 2=第2 1=第1
    for lhs, rhs in fds:
        if closure(lhs, fds) == R:
            continue              # 決定項がスーパーキーなら問題なし
        for a in sorted(rhs - lhs):
            if a not in prime and any(lhs < k for k in keys):
                why.append(f"部分関数従属 {fmt(lhs)}→{a}"); level = min(level, 1)
            elif a not in prime:
                why.append(f"推移的関数従属 {fmt(lhs)}→{a}"); level = min(level, 2)
            else:
                why.append(f"決定項がキーでない {fmt(lhs)}→{a}"); level = min(level, 3)
    nf = {4: "BCNF", 3: "第3正規形", 2: "第2正規形", 1: "第1正規形"}[level]
    return keys, nf, why

自作の4つの関係で動かした結果です(表示用の関数は省略しています)。

■ 受注明細(分解前)
  候補キー : {受注番号,明細番号}
  正規形   : 第1正規形
  理由     : 部分関数従属 受注番号→受注日
  理由     : 部分関数従属 受注番号→顧客コード
  理由     : 推移的関数従属 顧客コード→顧客名
  理由     : 推移的関数従属 商品コード→単価
  理由     : 推移的関数従属 商品コード→商品名
■ 受注(受注番号,受注日,顧客コード,顧客名)
  候補キー : 受注番号
  正規形   : 第2正規形
  理由     : 推移的関数従属 顧客コード→顧客名
■ 履修(学生,科目,教員)
  候補キー : {学生,教員} / {学生,科目}
  正規形   : 第3正規形
  理由     : 決定項がキーでない 教員→科目
■ 社員(社員番号,メール,氏名)
  候補キー : メール / 社員番号
  正規形   : BCNF
閉包 {受注番号}+ = ['受注日', '受注番号', '顧客コード', '顧客名']

判定の決まりを表にすると、次のとおりです。

正規形 満たす条件 違反の例(上の結果)
第1正規形 繰り返し項目が無く、値がそれ以上分けられない ―
第2正規形 非キー属性が候補キーの一部に従属しない(部分関数従属が無い) 受注番号→受注日
第3正規形 非キー属性が非キー属性に従属しない(推移的関数従属が無い) 顧客コード→顧客名
BCNF すべての関数従属性の決定項がスーパーキー 教員→科目

判定は、次の順に「ある・ない」をたどります。第1正規形ならキーの一部を決定項として、第2正規形なら非キーの決定項で分け、第3正規形以上なら BCNF かどうかを確かめます。

flowchart TD
    A[候補キーを求める] --> B{キーの一部に<br>従属する非キー}
    B -->|ある| N1[第1正規形]
    B -->|ない| C{非キーに従属<br>する非キー}
    C -->|ある| N2[第2正規形]
    C -->|ない| N3[第3正規形以上]

第3正規形に分解して、元に戻せるか確かめる

関数従属性の決定項ごとに表を分け、主キーと外部キーを付けました。

CREATE TABLE 顧客 AS SELECT DISTINCT 顧客コード, 顧客名 FROM 受注明細_非正規;
CREATE TABLE 商品 AS SELECT DISTINCT 商品コード, 商品名, 単価 FROM 受注明細_非正規;
CREATE TABLE 受注 AS SELECT DISTINCT 受注番号, 受注日, 顧客コード FROM 受注明細_非正規;
CREATE TABLE 受注明細 AS SELECT 受注番号, 明細番号, 商品コード, 数量 FROM 受注明細_非正規;
ALTER TABLE 顧客 ADD PRIMARY KEY (顧客コード);
ALTER TABLE 商品 ADD PRIMARY KEY (商品コード);
ALTER TABLE 受注 ADD PRIMARY KEY (受注番号), ADD FOREIGN KEY (顧客コード) REFERENCES 顧客;
ALTER TABLE 受注明細 ADD PRIMARY KEY (受注番号, 明細番号),
  ADD FOREIGN KEY (受注番号) REFERENCES 受注, ADD FOREIGN KEY (商品コード) REFERENCES 商品;

分解した表を結合し直して、元の表と1行も違わなければ「情報無損失分解」です。 EXCEPT で両方向の差を数えると、どちらも0行でした。

 元にあって結合に無い行 
------------------------
                      0
(1 row)

 結合にあって元に無い行 
------------------------
                      0
(1 row)

分解した後は、注文の無い商品も登録でき、顧客名の変更は1行で全部の受注に反映されました。

=> INSERT INTO 商品 VALUES ('P40','マウス',3000);
INSERT 0 1
=> UPDATE 顧客 SET 顧客名='青葉ホールディングス' WHERE 顧客コード='C01';
UPDATE 1
 受注番号 |        顧客名        
----------+----------------------
     1001 | 青葉ホールディングス
     1002 | 桜電機
     1003 | 青葉ホールディングス
(3 rows)

試験で注意したいのは「単価」の扱いです。ここでは「商品コード→単価」としましたが、業務で「受注のときの単価を残す」決まりがあれば、単価は受注明細の属性になります。 どちらになるかは状況記述で決まり、R7 午後Ⅰ問2の講評も「業務上から判断」することを求めています。

元に戻せない分解(偽の行が出る)

共通の列が、どちらの表のキーにもならない形で分けると、結合したときに元に無い行が出ます。明細と「商品コード・顧客コード」の組に分けて結合した結果です。

 受注番号 | 明細番号 | 商品コード | 顧客コード 
----------+----------+------------+------------
     1001 |        1 | P10        | C01
     1001 |        1 | P10        | C02
     1001 |        2 | P20        | C01
     1002 |        1 | P10        | C01
     1002 |        1 | P10        | C02
     1003 |        1 | P30        | C01
(6 rows)

元は4行なのに6行になり、「受注1001の顧客は C01 か C02 か」が分からなくなりました。

第3正規形とBCNFの違い(履修の例)

「学生は科目ごとに教員が1人」「教員は1科目だけを担当」という決まりの履修表は、候補キーが {学生,科目} と {学生,教員} の2つで、第3正規形ですが BCNF ではありません(上の Python の結果)。BCNF にするには「教員担当(教員, 科目)」と「学生教員(学生, 教員)」に分けます。

ところが分けた後は、「学生と科目が決まれば教員が1人に決まる」という決まりを、どちらの表の制約でも守れません。 佐藤さんに DB の教員をもう1人登録すると、エラーにならずに入りました。

=> INSERT INTO 学生教員 VALUES ('佐藤','山本');
INSERT 0 1
=> SELECT s.学生, t.科目, string_agg(s.教員, '・' ORDER BY s.教員) AS 教員
   FROM 学生教員 s JOIN 教員担当 t USING (教員) GROUP BY 1,2 HAVING count(*) > 1;
 学生 | 科目 |    教員    
------+------+------------
 佐藤 | DB   | 山本・田中
(1 row)

BCNF への分解は、関数従属性を保存できないことがあります。 第3正規形で止めるか BCNF まで進めるかは、この損失と更新時異状のどちらを避けたいかで決めます。正規化の基本は、関連記事「データベースの正規化をわかりやすく|第1〜第3正規形の例」で扱っています。

概念データモデル(ER図)と関係スキーマ

この節では、午後で最も多い題材である概念データモデルを、表の定義に落とすところまで確かめられます。

午後の問題では、概念データモデル(エンティティとリレーションシップの図)と関係スキーマ(受注(受注番号, 受注日, 顧客コード) のような表の形)の両方を完成させる設問がよく出ます。R6 午後Ⅰ問1の講評にあるとおり、主キーと外部キーは下線で示し、その誤りも採点されます。

上で分解した4つの表を、Mermaid の erDiagram で描くと次のとおりです。||--o{ は「1対0以上」、||--|{ は「1対1以上」を表します。

erDiagram
    direction TB
    顧客 ||--o{ 受注 : する
    受注 ||--|{ 受注明細 : 含む
    商品 ||--o{ 受注明細 : 載る
図の要素 関係スキーマでの表し方 例
エンティティ 表(関係)1つ 顧客(顧客コード, 顧客名)
1対多のリレーションシップ 多の側に、1の側の主キーを外部キーとして持つ 受注(受注番号, 受注日, 顧客コード)
多対多のリレーションシップ 連関エンティティ(両方の主キーを持つ表)を作る 受注明細(受注番号, 明細番号, 商品コード, 数量)
スーパータイプ・サブタイプ スーパータイプとサブタイプを同じ主キーで1対1につなぐ 法人取引先(取引先番号, 法人番号)
サブタイプの切り口 スーパータイプに「区分」の属性を持つ 取引先(取引先番号, 取引先区分, 名称)

スーパータイプとサブタイプを表にする

「取引先」は「法人」か「個人」のどちらか一方(排他)、という決まりを、表の制約で守る形です。図ではスーパータイプとサブタイプを「1対0か1」でつなぎます。

erDiagram
    direction TB
    取引先 ||--o| 法人取引先 : 法人
    取引先 ||--o| 個人取引先 : 個人

サブタイプ側にも区分の列を持たせ、(取引先番号, 取引先区分)の組で外部キーを張ります。

CREATE TABLE 取引先 (
  取引先番号 int PRIMARY KEY,
  取引先区分 text NOT NULL CHECK (取引先区分 IN ('法人','個人')),
  名称 text NOT NULL,
  UNIQUE (取引先番号, 取引先区分)
);
CREATE TABLE 法人取引先 (
  取引先番号 int PRIMARY KEY,
  取引先区分 text NOT NULL DEFAULT '法人' CHECK (取引先区分 = '法人'),
  法人番号 char(13) NOT NULL,
  FOREIGN KEY (取引先番号, 取引先区分) REFERENCES 取引先 (取引先番号, 取引先区分)
);
CREATE TABLE 個人取引先 (
  取引先番号 int PRIMARY KEY,
  取引先区分 text NOT NULL DEFAULT '個人' CHECK (取引先区分 = '個人'),
  生年月日 date NOT NULL,
  FOREIGN KEY (取引先番号, 取引先区分) REFERENCES 取引先 (取引先番号, 取引先区分)
);
INSERT INTO 取引先 VALUES (1,'法人','青葉商事'),(2,'個人','山田花子');
INSERT INTO 法人取引先 (取引先番号, 法人番号) VALUES (1,'1234567890123');
INSERT INTO 個人取引先 (取引先番号, 生年月日) VALUES (2,'1990-04-01');

個人の取引先2を、法人取引先にも登録しようとすると止まりました。

=> INSERT INTO 法人取引先 (取引先番号, 法人番号) VALUES (2,'9999999999999');
ERROR:  insert or update on table "法人取引先" violates foreign key constraint "法人取引先_取引先番号_取引先区分_fkey"
DETAIL:  Key (取引先番号, 取引先区分)=(2, 法人) is not present in table "取引先".

R7 午後Ⅰ問1の講評では、スーパータイプの関係スキーマに「サブタイプの切り口となる属性」(ここでの取引先区分)を書けない解答が多かったとされています。 切り口が複数あるときは、切り口ごとに区分の属性とサブタイプの組を作ります。ER図の記号の読み方は、関連記事「ER図の書き方|記号の読み方からCREATE TABLEまで実行して確認」で扱っています。

午後で問われるSQL(集約・相関サブクエリ・外部結合・ウィンドウ関数)

この節では、午後の SQL の設問でよく使う書き方を、結果つきで確かめられます。

上で分解した表に、受注を少し足して実行しました。

Q1 顧客ごとの売上合計(10万円以上だけ):GROUP BY と HAVING

SELECT k.顧客名, sum(s.単価 * m.数量) AS 売上
FROM 受注明細 m JOIN 受注 j USING (受注番号) JOIN 顧客 k USING (顧客コード) JOIN 商品 s USING (商品コード)
GROUP BY k.顧客名 HAVING sum(s.単価 * m.数量) >= 100000 ORDER BY 売上 DESC;
        顧客名        |  売上  
----------------------+--------
 青葉ホールディングス | 490000
 桜電機               | 210000
(2 rows)

Q2 一度も受注の無い顧客:NOT EXISTS(相関サブクエリ)

SELECT 顧客コード, 顧客名 FROM 顧客 k
WHERE NOT EXISTS (SELECT 1 FROM 受注 j WHERE j.顧客コード = k.顧客コード);
 顧客コード | 顧客名 
------------+--------
 C03        | 楓物産
(1 row)

Q3 受注の無い顧客も0件で出す:LEFT JOIN と count(列)

SELECT k.顧客コード, count(j.受注番号) AS 受注件数
FROM 顧客 k LEFT JOIN 受注 j ON j.顧客コード = k.顧客コード GROUP BY k.顧客コード ORDER BY 1;
 顧客コード | 受注件数 
------------+----------
 C01        |        3
 C02        |        2
 C03        |        0
(3 rows)

count(*) にすると、C03 は外部結合で作られた1行が数えられて1になります。count(列) は NULL を数えないので0になります。

Q4 顧客ごとの受注の順番・累計・前回の受注日:ウィンドウ関数

SELECT j.顧客コード, j.受注番号, j.受注日,
       sum(s.単価*m.数量) AS 金額,
       row_number() OVER w AS 何回目,
       sum(sum(s.単価*m.数量)) OVER w AS 累計,
       lag(j.受注日) OVER w AS 前回の受注日
FROM 受注 j JOIN 受注明細 m USING (受注番号) JOIN 商品 s USING (商品コード)
GROUP BY j.顧客コード, j.受注番号, j.受注日
WINDOW w AS (PARTITION BY j.顧客コード ORDER BY j.受注日)
ORDER BY 1, 3;
 顧客コード | 受注番号 |   受注日   |  金額  | 何回目 |  累計  | 前回の受注日 
------------+----------+------------+--------+--------+--------+--------------
 C01        |     1001 | 2026-10-01 | 330000 |      1 | 330000 | 
 C01        |     1003 | 2026-10-05 |  40000 |      2 | 370000 | 2026-10-01
 C01        |     1005 | 2026-10-07 | 120000 |      3 | 490000 | 2026-10-05
 C02        |     1002 | 2026-10-02 | 120000 |      1 | 120000 | 
 C02        |     1004 | 2026-10-06 |  90000 |      2 | 210000 | 2026-10-02
(5 rows)

sum(sum(...)) OVER は、GROUP BY で集計した値(内側の sum)を、さらにウィンドウで累計する(外側の sum)書き方です。

Q5 同じ値があるときの順位:RANK と DENSE_RANK

 商品コード | 数量 | rank | dense_rank 
------------+------+------+------------
 P40        |   10 |    1 |          1
 P20        |    5 |    2 |          2
 P30        |    5 |    2 |          2
 P10        |    4 |    4 |          3
(4 rows)

同じ数量が2つあると、RANK は次を4位に飛ばし、DENSE_RANK は3位にします。

Q6 部品構成を展開する:再帰 CTE

WITH RECURSIVE t(部品, 個数, 深さ) AS (
  SELECT 子, 個数, 1 FROM 部品構成 WHERE 親 = 'PC'
  UNION ALL
  SELECT c.子, t.個数 * c.個数, t.深さ + 1 FROM 部品構成 c JOIN t ON c.親 = t.部品)
SELECT repeat('  ', 深さ - 1) || 部品 AS 部品, 個数 FROM t ORDER BY 深さ, 部品;
    部品    | 個数 
------------+------
 キーボード |    1
 本体       |    1
   基板     |    1
     CPU    |    1
     メモリ |    2
(5 rows)

NOT IN と NOT EXISTS は、NULL があると結果が変わる

休止中の顧客の表に NULL が1行混ざっているとき、「休止中でない顧客」を2通りで求めました。

=> SELECT 顧客コード FROM 顧客 WHERE 顧客コード NOT IN (SELECT 顧客コード FROM 休止顧客);
 顧客コード 
------------
(0 rows)

=> SELECT 顧客コード FROM 顧客 k WHERE NOT EXISTS (SELECT 1 FROM 休止顧客 t WHERE t.顧客コード = k.顧客コード) ORDER BY 1;
 顧客コード 
------------
 C01
 C03
(2 rows)

相手の列に NULL があるとき

NOT IN0行(どの顧客も「NULL と等しくない」が UNKNOWN になる)
→
NOT EXISTSC01・C03 の2行(一致する行が無いかだけを見る)

NULL が入りうる列の否定は NOT EXISTS で書くと、意図どおりの結果になる

ウィンドウ関数の書き方は関連記事「SQLウィンドウ関数の使い方|累計・前月比・上位N件を実行結果で解説」で扱っています。

トランザクションと同時実行制御(分離レベル・ロック・デッドロック)

この節では、午後Ⅱで深く問われる分離レベルとロックの動きを、2つのセッションを同時に動かして確かめられます。

分離レベルで、読める値がどう変わるか

在庫の表を、セッション A が2秒あけて2回読み、その間(1秒後)にセッション B が更新して COMMIT しました。

分離レベル 起きる現象を試した操作 A の1回目 A の2回目
READ COMMITTED B が P10 の数量を 10→7 に更新 10 7(ノンリピータブルリード)
READ COMMITTED B が1万円以上の商品を1行追加 2件 3件(ファントム)
REPEATABLE READ B が P10 の数量を 10→7 に更新 10 10
REPEATABLE READ B が1万円以上の商品を1行追加 2件 2件

READ COMMITTED でノンリピータブルリードを起こしたときの出力です(A の出力は時刻順に並べ直しています)。

A: P10の数量 = 10
B: 1秒後に更新して COMMIT
A: P10の数量 = 7

SQL 標準では、REPEATABLE READ でもファントムは起きてよいとされていますが、PostgreSQL の REPEATABLE READ ではファントムも起きませんでした。 PostgreSQL のドキュメントの表も、REPEATABLE READ のファントムを「許容されるが PostgreSQL では起きない」としています。また、READ UNCOMMITTED を指定しても READ COMMITTED と同じ動きになります。別のセッションが COMMIT 前に 20→999 に変えた P20 を、READ UNCOMMITTED で読むと 20 のままでした。

A: P20の数量 = 20
分離レベル(SQL 標準) ダーティリード ノンリピータブルリード ファントム
READ UNCOMMITTED 起こりうる(PostgreSQL では起きない) 起こりうる 起こりうる
READ COMMITTED 起きない 起こりうる 起こりうる
REPEATABLE READ 起きない 起きない 起こりうる(PostgreSQL では起きない)
SERIALIZABLE 起きない 起きない 起きない

試験の問題は、特定の製品ではなく問題文に書かれた RDBMS の仕様で答えます。 手元の DBMS で動きを確かめるときは、その DBMS がどこまで標準と違うかを知っておくと、取り違えません。分離レベルの全体は関連記事「トランザクション分離レベルの違いをわかりやすく解説」で扱っています。

デッドロック

A は P10→P20、B は P20→P10 の順に、1秒あけて更新しました。お互いが相手の持つ行ロックを待つ形になり、PostgreSQL が片方(A)を止めました。

A| ERROR:  deadlock detected
A| DETAIL:  Process 28002 waits for ShareLock on transaction 791; blocked by process 28001.
A| Process 28001 waits for ShareLock on transaction 792; blocked by process 28002.
A| HINT:  See server log for query details.
A| CONTEXT:  while updating tuple (0,2) in relation "在庫"

B は COMMIT でき、A の更新は全部取り消されました(P10 と P20 は B の分だけ1ずつ減っていました)。PostgreSQL は、ロックを deadlock_timeout(既定 1000ms)待ったところでデッドロックかどうかを調べます。

flowchart LR
    A[セッションA<br>P10をロック済み] -->|P20を待つ| B[セッションB<br>P20をロック済み]
    B -->|P10を待つ| A

防ぎ方の基本は「どのトランザクションも、同じ順番で行をロックする」ことです。 R6 午後Ⅰ問2の講評も、トランザクションごとに必要なロックの種類と対象を読み取り、デッドロックを起こさない設計を求めていました。PostgreSQL での調べ方は関連記事「デッドロックの原因と対策|PostgreSQLで再現した実ログで解説」で扱っています。

ロック待ちの時間に上限を付ける

A が SELECT ... FOR UPDATE で行をロックしたまま3秒持ち、B は lock_timeout = '1s' を付けて同じ行を更新しました。

ERROR:  canceling statement due to lock timeout
CONTEXT:  while updating tuple (0,3) in relation "在庫"
Time: 1002.343 ms (00:01.002)

B は約1秒で待つのをやめ、エラーになりました。 lock_timeout の既定は 0(無制限に待つ)です。R6 午後Ⅰ問2のように「ロック待ちがタイムアウトしないか」を問う設問では、ロックを取ってから解放するまでの区間と、その長さを時系列で整理します。

時刻印(更新日時)による楽観的な制御

行に「更新時刻」を持たせ、読んだときの時刻のままなら更新する、という条件付きの UPDATE にします。読んだ後に別の人が先に更新すると、0行の更新になりました。

SELECT 数量, 更新時刻 AS 読んだ時刻 FROM 在庫2 WHERE 商品コード='P10' \gset
UPDATE 在庫2 SET 数量 = 数量 - 1, 更新時刻 = clock_timestamp() WHERE 商品コード='P10';   -- 他の人が先に更新
UPDATE 在庫2 SET 数量 = :数量 - 2, 更新時刻 = clock_timestamp()
 WHERE 商品コード='P10' AND 更新時刻 = :'読んだ時刻';
UPDATE 1
UPDATE 0
 数量 
------
    9

後から書いた方は UPDATE 0 になり、先の更新(10→9)は上書きされませんでした。 アプリは0行を見て、読み直してやり直します。R7 午後Ⅰ問3の講評にあるとおり、時刻印による制御は「具体的な処理の流れ」を想定すると答えやすくなります。

障害回復と2相コミット

この節では、異常終了の後にデータがどう戻るかと、2相コミットの「準備済み」の状態を確かめられます。

異常終了の後:COMMIT 済みは残り、COMMIT 前は消える

1行を COMMIT し、もう1行を入れたトランザクションが COMMIT する前に、pg_ctl stop -m immediate(後片付けをせずに止める)でサーバーを止めました。再起動のログと、再起動後の表です。

LOG:  database system was not properly shut down; automatic recovery in progress
LOG:  redo starts at 0/199B600
LOG:  redo done at 0/19BE7F0 system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
 id |    内容     
----+-------------
  1 | COMMIT 済み
(1 row)

COMMIT 済みの行は、ログ(WAL)を読み直して戻り(ロールフォワード)、COMMIT 前の行は残りませんでした。 試験の用語で言えば、障害の時点で確定していたトランザクションはログで「ロールフォワード」し、確定していなかったものは「ロールバック」した状態になる、という動きです。R6 午後Ⅱ問1では、再始動のときの具体的な処理と、ロールフォワードでの行の追加と更新のページの扱いの違いが問われています。

2相コミット:準備済みのトランザクションは再起動しても残る

2相コミットは、複数のデータベースにまたがる更新を「全部確定か、全部取り消しか」にする手順です。 第1相で各データベースに「確定できる状態にして待つ」(準備)を指示し、全員が準備できたら第2相で確定を指示します。PostgreSQL では PREPARE TRANSACTION が第1相、COMMIT PREPARED が第2相にあたります(max_prepared_transactions の既定は0なので、5にして起動しました)。

=> BEGIN;
=> UPDATE 在庫 SET 数量 = 0 WHERE 商品コード='P30';
=> PREPARE TRANSACTION 'order-1001';
=> SELECT gid, prepared::time(0) FROM pg_prepared_xacts;
    gid     | prepared 
------------+----------
 order-1001 | 18:48:41
(1 row)

この状態で異常終了させて再起動すると、準備済みのトランザクションは残っていて、行のロックも持ったままでした。

LOG:  recovering prepared transaction 795 from shared memory
    gid     
------------
 order-1001
(1 row)

=> SET lock_timeout='1s';
=> UPDATE 在庫 SET 数量 = 1 WHERE 商品コード='P30';
ERROR:  canceling statement due to lock timeout
=> COMMIT PREPARED 'order-1001';
=> SELECT 数量 FROM 在庫 WHERE 商品コード='P30';
 数量 
------
    0

2相コミットで守れること

準備済みの更新は、障害で再起動しても失われない

全員が準備できたときだけ確定する

2相コミットの弱み

確定の指示が来るまで、ロックを持ち続ける

調整役が止まると、準備済みのまま待ち続ける

R7 午後Ⅱ問1の講評では、2相コミットの基本的な流れに着目していない解答や、「一方のデータベースの障害で全体が止まる」という誤解が挙げられていました。PostgreSQL のドキュメントは、PREPARE TRANSACTION は外部のトランザクションマネージャーが使うためのもので、アプリや対話のセッションで使うものではないとしています。準備済みのトランザクションはロックを持ち続けるので、長く残さないことも注意として書かれています。

参照動作・物理設計と性能(RESTRICT・索引のクラスタ性・容量)

この節では、物理設計の設問で問われる参照動作・索引・ページ数を、数値で確かめられます。

ON DELETE RESTRICT と CASCADE

=> DELETE FROM 組織 WHERE 組織コード='D1';   -- 社員が ON DELETE RESTRICT で参照
ERROR:  update or delete on table "組織" violates foreign key constraint "社員_組織コード_fkey" on table "社員"
DETAIL:  Key (組織コード)=(D1) is still referenced from table "社員".
=> DELETE FROM 組織 WHERE 組織コード='D2';   -- 社員2 が ON DELETE CASCADE で参照
DELETE 1
 社員2の残り 
-------------
           0

RESTRICT は参照されている行の削除を止め、CASCADE は参照している行も一緒に消します。 R6 午後Ⅰ問3の講評は、RESTRICT の動きの理解がやや不足していたとしています。

索引のクラスタ性で、読むページ数が50倍変わる

20万行の表に、格納順と同じ順の列(伝票番号)と、ばらばらの列(顧客番号)を作り、それぞれの索引で約2,000行を読みました。比べやすいように、索引スキャンだけを使う設定にしています。

    列    | correlation 
----------+-------------
 伝票番号 |       1.000
 顧客番号 |      -0.001

 Index Scan using "ix_伝票" on "売上" (actual rows=2000 loops=1)
   Buffers: shared hit=34 read=5
 Index Scan using "ix_顧客" on "売上" (actual rows=1994 loops=1)
   Buffers: shared hit=1997 read=4
索引 格納順との相関 読んだ行 読んだページ
伝票番号(クラスタ性が高い) 1.000 2,000行 39ページ
顧客番号(クラスタ性が低い) -0.001 1,994行 2,001ページ

ほぼ同じ行数なのに、クラスタ性が低い索引は約50倍のページを読みました。 クラスタ性が高いと隣のキーが同じページに集まり、低いと1行ごとに別のページを読みに行くためです。R6 午後Ⅰ問3の講評も、この違いを表の数値を使って答えることを求めていました。設定を戻すと、PostgreSQL は顧客番号の検索に「Bitmap Heap Scan」(先に読むページを集めて並べてから読む方法)を選びました。

1ページに入る行数とページ数の見積り

同じ表で、1ページ(8,192バイト)に入った行数と、使ったページ数を数えました。

 1ページの最大行数 | 使用ページ数 
-------------------+--------------
                65 |         3077

 1行のバイト数 
---------------
           117

20万行÷65行=3,076.9 → 切り上げて3,077ページで、実際の数と一致しました。 試験の容量見積りも「1ページに入る行数(切り捨て)→ 必要なページ数(切り上げ)」の順に計算します。問題文に書かれたページの大きさ・行の長さ・空き領域の割合を使い、端数の扱いを取り違えないことが大切です。

午後(科目B)の答え方

この節では、記述式で点を取るための書き方が分かります。

IPA の採点講評で繰り返し書かれている内容を、答えるときの手順にしました。

場面 書き方 講評にある指摘
関係スキーマを書く 主キー・外部キーを下線で正しく示し、属性の漏れを確かめる 主キーの漏れ・下線の誤り(R6・R7)
リレーションシップを引く 状況記述の業務の流れ(何が何に対して起きるか)から引き先を決める 引き先の誤り(R6 問1・R7 午後Ⅱ問2)
正規化の根拠を書く 根拠にした関数従属性を書き、その従属性に沿って分解する 根拠と分解が合わない(R7 問2)
性能・索引を答える 一般論ではなく、本文の表の数値と事例で答える 一般論の解答が多い(R7 問3)
同時実行制御を答える 時系列の表にロックの取得と解放を書き込んで考える ロックの種類と対象を読み取れない(R6 問2)

午後Ⅰは90分で2問なので、1問あたり45分が目安になります。最初に3問の題材と設問をざっと見て、データモデル・SQL・トランザクションのうち得意な題材を選ぶと、時間を使い切らずに済みます。

よくある質問

Q. データベーススペシャリスト試験の難易度はどのくらいですか? A. 共通キャリア・スキルフレームワークのレベル4の高度試験で、合格率は直近4回とも17〜19%(令和7年度秋期は18.4%)です。4科目すべてで60点以上が必要で、記述式の科目Bが中心になります。

Q. 2026年度のデータベーススペシャリスト試験はいつですか? A. 2026年度は後期に CBT 方式で行われます。申込みは2027年1月27日〜2月27日、科目A-1・A-2は2月20日〜3月2日、科目B-1・B-2は3月16日〜3月28日、合格発表は2027年6月頃の予定です(2026年10月22日時点の IPA の案内)。

Q. 勉強時間はどのくらい必要ですか? A. IPA は目安を公表していません。応用情報技術者試験の範囲が身についているか、SQL・設計の業務経験があるかで変わります。「学習の順番と時間の目安」の節に、学ぶ順番と時間の配分の例を載せています。

Q. 過去問はどこで手に入りますか? A. IPA の過去問題のページに、問題冊子・解答例・採点講評(午後)が公開されています。ページには、利用に許諾や使用料は要らないと書かれています。午後は採点講評まで読むと、どこで点を落としやすいかが分かります。

Q. 午前Ⅰ(科目A-1)は免除できますか? A. 応用情報技術者試験の合格、高度試験か支援士試験の合格、高度試験・支援士試験の科目A-1(令和7年度までは午前Ⅰ)での基準点以上、のどれかで2年間免除されます。申込みのときに番号を入れないと免除になりません。

Q. 応用情報技術者試験に受かっていなくても受けられますか? A. 受けられます。年齢・学歴・実務経験や、他の試験の合格による受験の条件は、試験要綱に定められていません。応用情報技術者試験の合格は、科目A-1の免除の条件の1つです。

Q. データベーススペシャリスト試験は廃止されますか? A. IPA は、現行の試験制度は2026年度の実施で終わり、2027年度から新制度に移る予定としています。応用情報と高度試験は「プロフェッショナルデジタルスキル試験(仮称)」に再編される案ですが、データベーススペシャリストの個別の扱いは2026年10月22日時点では示されていません。

Q. CBT 方式で何が変わりますか? A. テストセンターのパソコンで、空席のある会場と日時を選んで受けます。科目Aと科目Bは別の日で、申込みのときに両方を予約します。記述式もキーボードとマウスで解答します。出題範囲・形式・問題数・時間は変えない予定です。

Q. 午後でSQLはどのくらい出ますか? A. 令和6・7年度の採点講評で SQL の書き方が指摘されたのは、午後Ⅰの R6 問3(ウィンドウ関数)と R7 問3(集計関数)です。どちらも SQL だけでなく、物理設計・同時実行制御・索引と組み合わせた問題でした。

Q. 合格率はどうやって計算されていますか? A. 合格者数÷受験者数です。令和7年度秋期は1,796人÷9,769人で18.4%でした。応募しても受けなかった人(令和7年度は約4,600人)は含まれません。

まとめ

  • データベーススペシャリスト試験は、IPA の高度試験(レベル4)。2026年度は2027年2〜3月に CBT 方式で行われ、申込みは2027年1月27日〜2月27日。
  • 科目A-1(50分)・A-2(40分)・B-1(90分)・B-2(120分)の4科目すべてで60点以上が合格。科目Aで60点に届かないと科目Bは採点されない。
  • 受験手数料は7,500円。科目A-1は応用情報の合格などで2年間免除。合格率は直近4回で17〜19%。
  • 現行制度は2026年度で終わり、2027年度から新制度に移る予定。
  • 令和6・7年度の午後10問は、概念データモデル・正規化が5問、SQL・同時実行制御・障害回復・物理設計が5問。
  • PostgreSQL 16 で、正規化の異状と情報無損失分解、BCNF の関数従属性の損失、NOT IN と NULL、ノンリピータブルリードとファントム、デッドロック、2相コミットの準備済み状態、索引のクラスタ性で約50倍のページ数の差を確かめた。

参考・出典

確認日はいずれも 2026年10月22日です。

  • IPA「データベーススペシャリスト試験」:https://www.ipa.go.jp/shiken/kubun/db.html
  • IPA「試験要綱・シラバスなど」(試験要綱 Ver.5.6):https://www.ipa.go.jp/shiken/syllabus/gaiyou.html
  • IPA「試験要綱 Ver.5.6」:https://www.ipa.go.jp/shiken/syllabus/rcu1hd00000141gq-att/youkou_ver5_6.pdf
  • IPA「データベーススペシャリスト試験(レベル4)シラバス Ver.4.1」:https://www.ipa.go.jp/shiken/syllabus/nq6ept00000014bm-att/syllabus_db_ver4_1.pdf
  • IPA「令和8年度 応用情報技術者試験、高度試験及び情報処理安全確保支援士試験の実施予定」:https://www.ipa.go.jp/shiken/2026/ap_koudo_sc_yotei.html
  • IPA「令和8年度 試験実施スケジュール(応用情報・高度・支援士)」:https://www.ipa.go.jp/shiken/2026/ap_koudo_sc_kikan.html
  • IPA「応用情報技術者試験、高度試験及び情報処理安全確保支援士試験におけるCBT方式での実施について」:https://www.ipa.go.jp/shiken/2026/ap_koudo_sc-cbt.html
  • IPA「科目A-1試験免除」:https://www.ipa.go.jp/shiken/about/koudo_menjo.html
  • IPA プレスリリース(2026年3月31日)「情報処理技術者試験の見直し」:https://www.ipa.go.jp/pressrelease/2025/press20260331.html
  • IPA「シラバス案(新試験)」:https://www.ipa.go.jp/shiken/syllabus/henkou/2026/20260630.html
  • IPA「令和7年度秋期 試験の合格者発表」:https://www.ipa.go.jp/news/2025/shiken_20251225.html
  • IPA「令和6年度秋期 試験の合格者発表」:https://www.ipa.go.jp/news/2024/shiken_20241226.html
  • IPA「令和5年度秋期 試験の合格者発表」:https://www.ipa.go.jp/news/2023/shiken/ex20231221.html
  • IPA「令和4年度秋期 試験の合格者発表」:https://www.ipa.go.jp/archive/press/2022/press20221222.html
  • IPA「過去問題」:https://www.ipa.go.jp/shiken/mondai-kaiotu/index.html
  • IPA「2025年度(令和7年度)問題冊子・配点割合・解答例・採点講評」:https://www.ipa.go.jp/shiken/mondai-kaiotu/2025r07.html
  • IPA「2024年度(令和6年度)問題冊子・配点割合・解答例・採点講評」:https://www.ipa.go.jp/shiken/mondai-kaiotu/2024r06.html
  • PostgreSQL 16 Documentation「13.2. Transaction Isolation」:https://www.postgresql.org/docs/16/transaction-iso.html
  • PostgreSQL 16 Documentation「PREPARE TRANSACTION」:https://www.postgresql.org/docs/16/sql-prepare-transaction.html
  • PostgreSQL 16 Documentation「20.12. Lock Management」:https://www.postgresql.org/docs/16/runtime-config-locks.html
  • PostgreSQL 16 Documentation「5.4. Constraints」:https://www.postgresql.org/docs/16/ddl-constraints.html

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「データベーススペシャリスト試験とは|日程・合格率・午後対策を実例で解説」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?