関係データベース(関係型データベース)とは?選択・射影・結合を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 | 坊っちゃん | 1001 | 1906 |
| 2 | こころ | 1001 | 1914 |
| 3 | 羅生門 | 1002 | 1915 |
| 4 | 蜘蛛の糸 | 1002 | 1918 |
| 5 | 舞姫 | 1003 | 1890 |
| 作者テーブル | |
|---|---|
| 作者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つの表にそろっているか。なければ共通の項目で結合する
- 欲しい行の条件は何か(選択の条件)
情報Ⅰのこの分野の要点と例題は情報通信ネットワークとデータの活用の要点と例題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とは?初心者でも分かる基礎知識にまとめています。







