3.SQLによる結合

概要

リレーショナルデータベースは複数のテーブルから構成される。リレーショナルデータベースでは,関連するデータが複数のテーブルに分散して格納されているため,複数のテーブルにまたがる情報を得るにはテーブルの結合が必要となる。この演習では,複数のテーブルを結合して1つのテーブルを生成する操作について説明し,SQLite3データベースとPythonを用いた結合操作の演習を行う。

外部キー

テーブル R(…, Ai, …) と S(…, Bj, …) について,属性 Bj がテーブル S の主キーであるとする。以下の条件が成り立つとき,属性 Ai をテーブル R の外部キーという。

R の任意の行における Ai の値は,S の Bj のいずれかの値と等しいか,または空値(NULL)である。

結合演算では,通常,外部キーとそれが参照する主キーを結合条件として使用する。

結合演算

R(A1, A2, …, An) と S(B1, B2, …, Bm) をテーブルとする。R の属性 Ai と S の属性 Bj に対する結合演算「R[Ai θ Bj]S」は次のように定義される(θ は比較演算子である)。

R[Ai θ Bj]S は,R と S の直積集合(R × S,すなわち R の各行と S の各行のすべての組み合わせ)の要素のうち,R.Ai θ S.Bj を満たすものの集合である。

結合演算「R[Ai θ Bj]S」は,SQLを用いて次のように記述できる。

SELECT * FROM R, S WHERE R.Ai θ S.Bj

結合問い合わせ

結合問い合わせとは,SQL問い合わせにおける WHERE <式> の中に結合演算を含む問い合わせのことである。

SQLite3のデータベースファイル

この授業では,リレーショナルデータベース管理システム SQLite3 を使用する。SQLite3 では,リレーショナルデータベースが1つのファイルに格納される。そのため,データベースファイルのコピーや配布が容易である。

UML図

ここでのUML(Unified Modeling Language)図は,リレーショナルデータベースのテーブル名,各テーブルの属性名,各属性のデータ型,主キーなどを図示したものである。この授業で使用するデータベースの構造を以下に示す。

凡例 — PK: 主キー,FK: 外部キー,UNIQUE: 一意制約

employees
emp_no : INTPK
birth_date : DATE
first_name : VARCHAR(14)
last_name : VARCHAR(16)
gender : ENUM('M','F')
hire_date : DATE
departments
dept_no : CHAR(4)PK
dept_name : VARCHAR(40)UNIQUE
dept_emp
emp_no : INTPK, FK → employees
dept_no : CHAR(4)PK, FK → departments
from_date : DATE
to_date : DATE
dept_manager
emp_no : INTPK, FK → employees
dept_no : CHAR(4)PK, FK → departments
from_date : DATE
to_date : DATE
titles
emp_no : INTPK, FK → employees
title : VARCHAR(50)PK
from_date : DATEPK
to_date : DATE
salaries
emp_no : INTPK, FK → employees
salary : INT
from_date : DATEPK
to_date : DATE

テーブル間の関連: employees ←1:N→ dept_emp ←N:1→ departments,employees ←1:N→ dept_manager ←N:1→ departments,employees ←1:N→ titles,employees ←1:N→ salaries

出典: MySQL Employees Sample Database
https://dev.mysql.com/doc/employee/en/sakila-structure.html より引用

演習準備

この演習では Python を使用する。Python がインストールされていない場合は,下記の「Python 3.12 のインストール(Windows 上)」を展開し,手順に従いインストールすること。

Python 3.12 のインストール(Windows 上) [クリックして展開]

以下のいずれかの方法で Python 3.12 をインストールする。Python がインストール済みの場合、この手順は不要である。

方法1:winget によるインストール

管理者権限コマンドプロンプトで以下を実行する。管理者権限のコマンドプロンプトを起動するには、Windows キーまたはスタートメニューから「cmd」と入力し、表示された「コマンドプロンプト」を右クリックして「管理者として実行」を選択する。

winget install -e --id Python.Python.3.12 --scope machine --silent --accept-source-agreements --accept-package-agreements --override "/quiet InstallAllUsers=1 PrependPath=1 AssociateFiles=1 InstallLauncherAllUsers=1"

--scope machine を指定することで、システム全体(全ユーザー向け)にインストールされる。このオプションの実行には管理者権限が必要である。インストール完了後、コマンドプロンプトを再起動すると PATH が自動的に設定される。

方法2:インストーラーによるインストール

  1. Python 公式サイト(https://www.python.org/downloads/)にアクセスし、「Download Python 3.x.x」ボタンから Windows 用インストーラーをダウンロードする。
  2. ダウンロードしたインストーラーを実行する。
  3. 初期画面の下部に表示される「Add python.exe to PATH」に必ずチェックを入れてから「Customize installation」を選択する。このチェックを入れ忘れると、コマンドプロンプトから python コマンドを実行できない。
  4. 「Install Python 3.xx for all users」にチェックを入れ、「Install」をクリックする。

インストールの確認

コマンドプロンプトで以下を実行する。

python --version

バージョン番号(例:Python 3.12.x)が表示されればインストール成功である。「'python' は、内部コマンドまたは外部コマンドとして認識されていません。」と表示される場合は、インストールが正常に完了していない。

演習

ステップ1:データベースの準備

以下の手順で,Employees Sample データベース(SQLite版)を準備する。

このデータベースは,MySQL Employees Sample Database を SQLite 形式に変換したものであり,GitHub 上の bytebase/employee-sample-database リポジトリで公開されている。

コマンドプロンプトで以下のコマンドを実行し,データベースファイルをカレントディレクトリにダウンロードする。python -c は,引用符内の Python コードをコマンドライン上で直接実行するオプションである。urllib.request.urlretrieve は,第1引数に指定した URL からファイルを取得し,第2引数に指定したファイル名で保存する関数である。

python -c "import urllib.request; urllib.request.urlretrieve('https://raw.githubusercontent.com/bytebase/employee-sample-database/main/sqlite/dataset_small/employee.db', 'employee.db')"

ステップ2:SQL演習

Pythonの対話型シェルを使って,SQLによるデータ操作を実行する。

Pythonの対話型シェルは,コマンドプロンプト(Windows)またはターミナル(macOS / Linux)で python と入力して起動する。sqlite3 モジュールはPythonの標準ライブラリに含まれるため,追加のインストールは不要である。

① コマンドプロンプトまたはターミナルを開き,python と入力してPythonの対話型シェルを起動する。

python

② 次のPythonプログラムを実行し,SQLite3データベースに接続する。この演習で使用するデータベースファイルは,ステップ1でカレントディレクトリにダウンロードした employee.db である。

import sqlite3
c = sqlite3.connect('employee.db')

③ データベースに含まれるテーブルの一覧を確認する。sqlite_master は SQLite が内部的に保持するシステムテーブルであり,データベース内のテーブルやインデックスなどの定義情報が格納されている。type='table' の条件を指定することで,テーブル名のみを取得できる。

result = c.execute("select name from sqlite_master where type='table'").fetchall()
print(result)

④ 各テーブルの列情報を確認する。PRAGMA table_info(テーブル名) は,指定したテーブルの列名,データ型,主キーの有無などの構造情報を返す SQLite 固有のコマンドである。

for table_name in ['department', 'dept_manager', 'dept_emp', 'employee', 'title', 'salary']:
    print(table_name)
    result = c.execute(f"PRAGMA table_info({table_name})").fetchall()
    print(result)
    print()

⑤ 次のPythonプログラムを実行し,テーブルの全データを読み出す。データの読み出しには,SQL文の実行と結果の取得を行うオブジェクトであるカーソルを使用する。

result = c.execute("select * from department").fetchall()
print(result)

続けて,以下も実行する。

result = c.execute("select * from dept_manager").fetchall()
print(result)

実行結果から,例えば部門番号 d001 のマネージャが交替した時期などが確認できる。

⑥ 次のPythonプログラムを実行し,条件に合致する行を読み出す。salary テーブルの amount 列は給与額を表す。

result = c.execute("select * from salary where amount > 155000").fetchall()
print(result)

⑦ 結合問い合わせを実行する。amount > 155000 を満たす従業員の氏名などを取得する。ここでは,salary テーブルと employee テーブルを従業員番号(emp_no)で結合する。SQL文を複数行で記述するため,Pythonの三重引用符 """ を使用する。

result = c.execute("""select *
    from salary, employee
    where salary.emp_no = employee.emp_no
    and amount > 155000""").fetchall()
print(result)

続けて,高給与の従業員の職位情報を得るために,salary テーブルと title テーブルを従業員番号(emp_no)で結合する以下の問い合わせも実行する。

result = c.execute("""select *
    from salary, title
    where salary.emp_no = title.emp_no
    and amount > 155000""").fetchall()
print(result)

⑧ 接続を閉じる

c.close()