関係データベース(関係型データベース)とは?選択・射影・結合をSQLの実行例で解説【情報I】

関係データベース(関係型データベース、リレーショナルデータベース)とは、データを行と列からなる表(テーブル)で管理し、複数の表を共通の項目で関連付けて使うデータベースです。表からデータを取り出す基本の操作は「選択」(条件に合う行を取り出す)、「射影」(必要な列だけを取り出す)、「結合」(複数の表を1つにまとめる)の3つです。

高校の「情報Ⅰ」でも、この3つの操作を学びます。文部科学省の高等学校情報科「情報Ⅰ」教員研修用教材(第4章)は、リレーショナルデータベースでは「選択」「射影」「結合」という関係演算を用いて処理することが多いと説明し、それぞれをSQLで書いた例を載せています。この記事では、同じ考え方の小さな蔵書データを作り、3つの操作をSQLで実際に動かして結果を確かめます。

なお、この記事の旧版では3つ目の操作を「統合(UNION)」として説明していましたが、情報Ⅰで学ぶ3つの操作は「選択・射影・結合」です。UNIONについては後半で、結合との違いとして扱います。

関係データベースの基本用語

用語意味表でいうと
テーブル(表、リレーション)同じ種類のデータの集まり1枚の表
レコード(行、タプル)1件分のデータ横の1行
フィールド(列、属性)データの項目(書籍名、作者名など)縦の1列
主キーレコードを1件に特定できる項目書籍ID、作者IDなど
外部キーほかの表の主キーを参照する項目書籍テーブルの作者ID

関係データベースを操作する言語がSQL(Structured Query Language)です。文部科学省の教材では、無料で使える製品の例としてMySQLやPostgreSQLを挙げています。この記事では、Pythonに標準で入っているSQLiteを使います。

例に使う2つのテーブル

書籍の情報と作者の情報を、別々のテーブルに分けて持ちます。書籍テーブルには作者名を書かず、作者IDだけを入れておきます。

書籍テーブル
書籍ID書籍名作者ID出版年
1坊っちゃん10011906
2こころ10011914
3羅生門10021915
4蜘蛛の糸10021918
5舞姫10031890
作者テーブル
作者ID作者名
1001夏目漱石
1002芥川龍之介
1003森鴎外

表を分けておくと、同じ作者名を何度も書かずに済み、作者名を直すときも作者テーブルの1か所を変えるだけで済みます。このように重複をなくす表の設計を「正規化」といいます(詳しくはデータベースの正規化とは?で解説しています)。

次のPythonコードでテーブルを作り、3つの操作を順に実行します。

import sqlite3
con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.executescript("""
CREATE TABLE 作者 (作者ID INTEGER PRIMARY KEY, 作者名 TEXT);
CREATE TABLE 書籍 (書籍ID INTEGER PRIMARY KEY, 書籍名 TEXT, 作者ID INTEGER, 出版年 INTEGER);
INSERT INTO 作者 VALUES (1001, '夏目漱石'), (1002, '芥川龍之介'), (1003, '森鴎外');
INSERT INTO 書籍 VALUES
 (1, '坊っちゃん', 1001, 1906),
 (2, 'こころ', 1001, 1914),
 (3, '羅生門', 1002, 1915),
 (4, '蜘蛛の糸', 1002, 1918),
 (5, '舞姫', 1003, 1890);
""")
def show(title, sql):
    print("--", title)
    for row in cur.execute(sql):
        print(row)

選択:条件に合う行を取り出す

選択は、テーブルから条件を満たすレコード(行)だけを取り出す操作です。SQLではWHERE句で条件を書きます。関係代数の記号ではσ(シグマ)で表します。

show("選択", "SELECT * FROM 書籍 WHERE 作者ID = 1001")
-- 選択
(1, '坊っちゃん', 1001, 1906)
(2, 'こころ', 1001, 1914)

行は減りますが、列(書籍ID・書籍名・作者ID・出版年)はすべて残ります。「表を横に切る」操作だと覚えるとわかりやすいです。

射影:必要な列だけを取り出す

射影は、テーブルから一部のフィールド(列)だけを取り出す操作です。SQLではSELECTの後ろに列名を並べます。関係代数の記号ではπ(パイ)です。

show("射影", "SELECT 書籍名, 出版年 FROM 書籍")
-- 射影
('坊っちゃん', 1906)
('こころ', 1914)
('羅生門', 1915)
('蜘蛛の糸', 1918)
('舞姫', 1890)

今度は行がすべて残り、列が減ります。「表を縦に切る」操作です。

射影と重複:SQLでは自動で消えない

理論上の関係代数では、射影の結果に同じ行があれば1つにまとめます。ところがSQLのSELECTは、重複をそのまま残します。重複を消したいときはDISTINCTを付けます。

show("射影(作者IDだけ)", "SELECT 作者ID FROM 書籍")
show("射影+DISTINCT", "SELECT DISTINCT 作者ID FROM 書籍")
-- 射影(作者IDだけ)
(1001,)
(1001,)
(1002,)
(1002,)
(1003,)
-- 射影+DISTINCT
(1001,)
(1002,)
(1003,)

試験問題で「射影した結果は何行になるか」と問われたら、問題文が重複をどう扱う前提なのかを確かめてください。

結合:複数の表を1つにまとめる

結合は、複数のテーブルを共通の項目でつないで1つの表にする操作です。書籍テーブルには作者名がありませんが、作者IDが等しい行どうしをつなげば、書籍名と作者名を並べた表が作れます。SQLではJOINを使います。

show("結合", "SELECT 書籍.書籍名, 作者.作者名 FROM 書籍 INNER JOIN 作者 ON 書籍.作者ID = 作者.作者ID")
-- 結合
('坊っちゃん', '夏目漱石')
('こころ', '夏目漱石')
('羅生門', '芥川龍之介')
('蜘蛛の糸', '芥川龍之介')
('舞姫', '森鴎外')

この例は、結合したあとに書籍名と作者名の2列だけを射影しています。INNER JOINは、両方の表に対応する行があるものだけを残す結合です。対応がない行も残したい場合はLEFT JOINなどを使います(JOINの種類はSQLとは?基本の書き方で説明しています)。

3つの操作を組み合わせる

実際の問い合わせ(クエリ)では、選択・射影・結合を1つのSQLで同時に使います。「1910年以降に出版された本の書籍名と作者名」を取り出してみます。

show("組み合わせ", "SELECT 書籍.書籍名, 作者.作者名 FROM 書籍 INNER JOIN 作者 ON 書籍.作者ID = 作者.作者ID WHERE 書籍.出版年 >= 1910")
-- 組み合わせ
('こころ', '夏目漱石')
('羅生門', '芥川龍之介')
('蜘蛛の糸', '芥川龍之介')
操作何をするかSQLの書き方表の変化
選択条件に合う行を取り出すWHERE 条件行が減る
射影必要な列を取り出すSELECT 列名, 列名列が減る
結合表どうしを共通の項目でつなぐJOIN … ON 条件列が増える

結合(JOIN)と和(UNION)の違い

「表を1つにまとめる」という点で結合と混同しやすいのが、集合演算の和(SQLのUNION)です。結合は表を横につないで列を増やすのに対し、和は列の形が同じ表を縦につないで行を増やします。

import sqlite3
con = sqlite3.connect(":memory:")
con.executescript("""
CREATE TABLE 図書室A (書籍名 TEXT);
CREATE TABLE 図書室B (書籍名 TEXT);
INSERT INTO 図書室A VALUES ('こころ'), ('羅生門');
INSERT INTO 図書室B VALUES ('羅生門'), ('舞姫');
""")
print("UNION    :", con.execute("SELECT 書籍名 FROM 図書室A UNION SELECT 書籍名 FROM 図書室B ORDER BY 書籍名").fetchall())
print("UNION ALL:", con.execute("SELECT 書籍名 FROM 図書室A UNION ALL SELECT 書籍名 FROM 図書室B").fetchall())
UNION    : [('こころ',), ('羅生門',), ('舞姫',)]
UNION ALL: [('こころ',), ('羅生門',), ('羅生門',), ('舞姫',)]

UNIONは重複を1つにまとめ、UNION ALLは重複も残します。関係代数には、和のほかに差(EXCEPT)や積(INTERSECT)、直積といった集合演算もありますが、情報Ⅰでまず押さえるべきなのは選択・射影・結合の3つです。

実行環境:Python 3.9 に付属のsqlite3モジュール(SQLite 3.51.0)。行の並び順は、ORDER BYを付けない限りSQLの規格では保証されません。

情報Ⅰ・共通テストでの出方

情報Ⅰの「情報通信ネットワークとデータの活用」の分野で、関係データベースと選択・射影・結合が扱われます。試験では、表を見せて「この操作をすると結果はどうなるか」「求める表を作るにはどの操作が必要か」を問う形が考えられます。次の手順で考えると迷いません。

  1. 最終的に欲しい表の列は何か(射影で残す列)
  2. その列が1つの表にそろっているか。なければ共通の項目で結合する
  3. 欲しい行の条件は何か(選択の条件)

情報Ⅰのこの分野の要点と例題は情報通信ネットワークとデータの活用の要点と例題15問にまとめています。

テックジム東京本校では、情報科目の受験対策指導もご用意しています。

よくある質問

Q. 関係型データベースとリレーショナルデータベースは同じものですか?

同じものです。関係データベース、関係型データベース、リレーショナルデータベース(RDB)はいずれも、データを表で管理し、表どうしを関連付けて使うデータベースを指します。

Q. 選択と射影の違いは何ですか?

選択は条件に合う行を取り出す操作で、行が減ります。射影は必要な列を取り出す操作で、列が減ります。SQLでは選択がWHERE句、射影がSELECTの後ろの列名にあたります。

Q. 結合はSQLでどう書きますか?

JOINを使い、ONの後ろにつなぐ条件を書きます。たとえば「SELECT * FROM 書籍 INNER JOIN 作者 ON 書籍.作者ID = 作者.作者ID」で、作者IDが等しい行どうしをつないだ表が得られます。

Q. 射影すると重複した行は消えますか?

関係代数の理論では重複は1つにまとめられますが、SQLのSELECTでは重複が残ります。SQLで重複を消すにはDISTINCTを付けます。

Q. 「統合」という操作はありますか?

情報Ⅰで学ぶ関係データベースの3つの基本操作は「選択・射影・結合」です。表を縦につなぐ操作は集合演算の「和」で、SQLではUNIONを使います。

まとめ

  • 関係データベースは、データを表で持ち、共通の項目で表どうしを関連付けて使う
  • 選択はWHEREで行を絞り、射影はSELECTで列を絞り、結合はJOINで表をつなぐ
  • SQLの射影では重複が残るので、消したいときはDISTINCTを使う
  • UNION(和)は表を縦につなぐ集合演算で、結合とは別の操作

【情報I】受験対策・お悩み相談会(オンライン・無料)

SQLとデータベースの基礎はSQLとは?初心者でも分かる基礎知識にまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら