テーブルの分解 (table decomposition)
【概要】
テーブルの分解は,あるテーブルを属性集合の部分集合による射影で置き換えるプロセスであり,X₁∪X₂∪…∪Xₘ = {A₁, A₂,…, Aₙ}を満たす.情報無損失分解は,分解されたテーブルから元のテーブルを復元できる分解である.関数従属性はリレーションスキーマの属性間の依存関係を表し,ある属性の値が決まれば別の属性の値が一意に決まる関係である.ボイス・コッド正規形は,任意の関数従属性X→Yについて,Xがスキーマのキーになっていると定義される.ボイス・コッド正規形は更新時異状を解消できるが,正規化前のテーブルにおける関数従属性を維持できない場合がある.
【目次】
- 用語
- 演習で行うこと
- SQLite Manager Tool の起動と終了
- SQLite Manager Tool で新しいデータベースを作成する
- SQLite Manager Tool で既存のデータベースを開く
- SQL を用いたテーブル定義と一貫性制約の記述
- SQL を用いたテーブルへの行の挿入
- SQLite Manager Tool を用いたデータのブラウズ
- DISTINCT を含む SQL の例
- SQL を用いたテーブルの分解
- 結合問い合わせ (join query)
- 情報無損失分解にならない分解
- 演習問題と解答例
【サイト内の関連ページ】
公開資料は クリエイティブコモンズ 表示-非営利-継承 4.0 国際ライセンス(CC BY-NC-SA 4.0) で提供しており,事前の許可なく自由に利用できます.条件は著作者表示(BY),非営利目的のみ(NC),同一ライセンスでの再配布(SA)です.
用語
- テーブル(table)
リレーショナルデータベース での「テーブル」は,多重集合 (multi-set)を基礎とする体系になっている ことに注意してほしい. 「テーブル」は,同じ値をもった行が複数回現れることが許される.
- SQL におけるDISTINCT 指定
SQL では,SELECT と選択リストとの間に「DISTINCT」と書く場合がある.これを DISTINCT 指定という.
DISTINCT 指定を行うと,問い合わせの結果に重複行があるとき1つを残して他を除去する. 重複行というときは,行全体が同じ値だという意味である. 下記の例では,同じ注文者名が別の商品番号の注文を行った場合には重複行ではない.
select distinct 商品番号, 注文者名 FROM 注文;DISTINCT 指定があるとき,重複行があれば1つを残して他を除去するから,SQL の評価結果は多重集合になることはない(必ず集合になる). 一方で,DISTINCT 指定がないときは,SQL の評価結果が多重集合になる場合がある.
補足説明する. 下記のテーブル scores を例に説明する.
id | name | teacher_name | student_name | score ---------------------------------------------------- 1 | Database | K | KK | 85 2 | Database | K | AA | 75 3 | Database | K | LL | 90 4 | Programming | A | KK | 85 5 | Programming | A | LL | 75このテーブル scores から,属性 name と teacher_name だけを抜き出して新しいテーブルを作ったとする.下記のように重複行が現れる.つまり新しくできたテーブルは多重集合である.
name | teacher_name --------------------------- Database | K Database | K Database | K Programming | A Programming | A上記のテーブルを作るための SQL は次のとおりである.
SELECT name, teacher_name FROM scores;一方で,射影 scores[name, teacher_name] といったときは,属性 name と teacher_name だけを抜き出した上で, 重複行があるとき1つを残して他を除去する.結果は次のとおりである.
name | teacher_name --------------------------- Database | K Programming | A上記のテーブルを得るための SQL には, 次のように DISTINCT 指定が付く.
SELECT DISTINCT name, teacher_name FROM scores; - 結合属性
R(A1, A2, …, An) と S(B1, B2, …, Bm) をリレーションとする.この2つのリレーションの共通属性 C1, C2, …, Ck は次の性質を満たす.
- {A1, A2, …, An}∩{B1, B2, …, Bm} = {C1, C2, …, Ck}
- 各Ci(1≦i≦k)について,元のリレーションRとSにおけるドメインが等しい.つまり dom(R.Ci) = dom(S.Ci)
共通属性のことを結合属性ともいう.
- 自然結合演算
R(A1, A2, …, An) と S(B1, B2, …, Bm) をリレーションとする.RとSの自然結合演算を「R * S」と書く.自然結合演算は,結合演算と,射影演算を使って,次のように表すことができる.
R * S = (R[C1 = C1, C2 = C2, …, Ck = Ck]S) [A1, A2, …, An, D1, D2, …, Dm-k], 但し,C1, C2, …, Ck は,AとB の結合属性. また,{B1, B2, …, Bm}−{C1, C2, …, Ck} = {D1, D2, …, Dm-k}とする.*「R * S」の代わりに「R S」のように書くこともある.
- テーブルの分解
あるテーブルの テーブル名が R,属性名が A1, A2, …, An であるとする.また,このテーブルには 重複行が無いものと仮定する. このテーブルの,m 個のテーブル X1, X2, ..., Xm への分解は次のように定義される.
X1, X2, …, Xm をR の全属性集合{A1, A2,…, An}の部分集合で, X1∪X2∪…∪Xm = {A1, A2,…, An}なる条件を満たすとき(XiとXjは共通集合をもってよい),テーブルを m 個の射影 R[X1], R[X2], …, R[Xm] で置き換えることを,このテーブルの分解という.* 重複行を持つようなテーブルについては,分解を考えないことにする(重複行を持つテーブルについては,情報無損失分解であることの定義が難しいから).
- リレーションの情報無損失分解
あるリレーションが,その分解成分の自然結合をとると元の形に復元できるとき,その分解は情報無損失分解 (information lossless decomposition) であるという.
- 更新時異状
更新時異状には,修正時異状,挿入時異状,削除時異状の3種類がある.データベース設計時の配慮によって,更新時異状のかなりの部分を防ぐことができる.
- 修正時異状
修正時異状は,リレーションのタプルの属性値の修正において発生する異状のことである.
実世界における変化を,あるリレーションのタプルの属性値の修正において反映させたいとき,リレーションの設計によっては,リレーションの中の複数のタプルを同時に更新しなければならないことがある.これは,複数のタプルを同時に更新することを忘れ,データベースを壊してしまう可能性を生む.
- 挿入時異状
挿入時異状は,リレーションへのタプルの挿入において発生する異状のことである.
非空制約あるいはキー制約があると,その制約が付与されている属性の属性値が空値であるようなタプルは挿入できない.そのことが原因で,実世界における変化を,あるリレーションへのタプルの挿入によってデータベースに反映させたいと思っても,それができないことがある.
- 削除時異状
削除時異状は,リレーションからのタプルの削除において発生する異状のことである.
実世界における変化を,あるリレーションからのタプルの削除によってデータベースに反映させようとしたとき,そのタプルの削除によって,他の有用な情報が失われることがある.
- 関数従属性
リレーションスキーマR(A1, A2, …, Al, B1, B2, …, Bm, C1, C2, …, Cn) に関数従属性A1, A2, …, Al → B1, B2, …, Bm が存在するとは,次の場合をいう.
・リレーションスキーマRの任意のインスタンスの任意の2タプルt と t' について,それらのA1, A2, …, Al値が等しければ,B1, B2, …, Bm 値が等しいということが必ず成り立つ
(補足) リレーションスキーマR(A1, A2, …, Al, B1, B2, …, Bm, C1, C2, …, Cn) に関数従属性A1, A2, …, Al → B1, B2, …, Bm が存在することは,リレーションスキーマRの任意のインスタンスRが,2つの射影R[A1, A2, …, Al, B1, B2, …, Bm] とR[A1, A2, …, Al, C1, C2, …, Cn] に情報無損失分解されるための十分条件になるという性質がある.
- ボイス・コッド正規形 (Boyce-Codd normal form)
リレーションスキーマRがボイス・コッド正規形であるとは,次の条件が成立することである.
・Rが持つ任意の関数従属性X→Yについて(複数の関数従属性があるときは,その全てについて),XがR のキーになっている
まとめると,ボイス・コッド正規形について,次のことが言える.
- 任意のリレーションスキーマを情報無損失分解して,ボイス・コッド正規形にできる
- ボイス・コッド正規形になるように情報無損失分解することで,更新時異状を解消できる
- しかし,分解前のリレーションが保持していた関数従属性を,ボイス・コッド正規形に分解後のリレーションが維持できなくなる可能性がある.つまり,ボイス・コッド正規形に分解後のリレーションでは,元のリレーションが保持していた関数従属性に抵触するような更新操作を防げなくなる可能性がある.
- 単純値 (atomic value)
分解不可能な値のことである. 直積集合の要素や,べき集合の要素は単純値ではないと考えるのが普通である.
- 直積集合の例:属性名「family name」のドメインと,属性名「given name」のドメインの直積集合
- べき集合の例:ドメイン {みかん,りんご,バナナ}から構成されるべき集合は,{φ, {みかん}, {りんご}, {バナナ}, {みかん, りんご}, {りんご, バナナ}, {バナナ, みかん}, {みかん, りんご, バナナ}}
演習で行うこと
- SQLite Manager Tool の起動と終了
- SQLite Manager Tool で既存のデータベースを開く
- SQL を用いたテーブル定義と一貫性制約の記述
リレーショナル・スキーマ (relational schema): scores(id, name, teacher_name, student_name, score, created_at, updated_at)
SQL プログラム:
create table scores ( id integer primary key autoincrement not null, name text not null, teacher_name text not null, student_name text not null, score integer not null check ( score >= 0 AND score <=100 ), created_at datetime not null, updated_at datetime, unique (name, student_name) ); - SQL を用いたテーブルへの行の挿入
begin transaction; insert into scores values( 1, 'Database', 'K', 'KK', 85, datetime('now', 'localtime'), NULL ); insert into scores values( 2, 'Database', 'K', 'AA', 75, datetime('now', 'localtime'), NULL ); insert into scores values( 3, 'Database', 'K', 'LL', 90, datetime('now', 'localtime'), NULL ); insert into scores values( 4, 'Programming', 'A', 'KK', 85, datetime('now', 'localtime'), NULL ); insert into scores values( 5, 'Programming', 'A', 'LL', 75, datetime('now', 'localtime'), NULL ); commit;datetime('now', 'localtime') は現在日時の取得である.datetime型は,YYYY-MM-DD HH:MM:SS形式である.
- SQLite Manager Tool を用いたデータのブラウズ
- DISTINCT を含む SQL の例
SELECT DISTINCT name, teacher_name FROM scores; SELECT name, teacher_name FROM scores; SELECT DISTINCT score FROM scores; SELECT score FROM scores; - SQL を用いたテーブルの分解
create table A AS select distinct name, teacher_name FROM scores; create table B AS select distinct id, name, student_name, score FROM scores; - 結合問い合わせ (join query)
SELECT B.id, A.name, A.teacher_name, B.student_name, B.score FROM A, B WHERE A.name = B.name; - 情報無損失分解にならない分解
create table C AS select distinct name, student_name FROM scores; create table D AS select distinct id, teacher_name, student_name, score FROM scores; SELECT D.id, C.name, D.teacher_name, C.student_name, D.score FROM C, D WHERE C.student_name = D.student_name;
SQLite Manager Tool の起動と終了
- SQLite Manager Tool の起動
* Ubuntu での SQLite Manager Tool の起動例
「プログラミング」→「SQLite Manager Tool」と操作する.
SQLite Manager Tool の新しいウインドウが開く.
* Lubuntu での SQLite Manager Tool の起動例
「プログラミング」→「SQLite Manager Tool」と操作する.
SQLite Manager Tool の新しいウインドウが開く.
* Windows での SQLite Manager Tool の起動例
「SQLite Manager Tool」 のアイコンをダブルクリックする.
SQLite Manager Tool のウインドウが開く.
SQLite Manager Tool で新しいデータベースを作成する
前回の授業で作成したデータベースを使うときは,ここの手順は行わなくてよい.
以下の手順で,新しいデータベースを作成する.その結果,データベースファイルができる.
- ツールバーの「新規DB作成」ボタンをクリックする.
- データベースの新規作成を開始する
データベースファイル名 を指定する.
SQLite Manager Tool で既存のデータベースを開く
すでに作成済みのデータベースを,下記の手順で開く.
- ツールバーの「DBオープン (Ctrl+O)」ボタンをクリックする.
- データベースファイルを開く
* Ubuntu での実行例(「mydb」を開く場合)
データベースファイル mydb を選び, 「開く」をクリックする.
* Windows での実行例(「C:\SQLite\mydb」を開く場合)
データベースファイル C:\SQLite\mydb を選び, 「開く」をクリックする.
SQL を用いたテーブル定義と一貫性制約の記述
SQL を用いて,scores テーブルを定義し,一貫性制約を記述する.
リレーショナル・スキーマ (relational schema): scores(id, name, teacher_name, student_name, score, created_at, updated_at)
- scores テーブルの定義
次の SQL を入力し,「▶ SQL実行 (F5)」を押下する
create table scores ( id integer primary key autoincrement not null, name text not null, teacher_name text not null, student_name text not null, score integer not null check ( score >= 0 AND score <=100 ), created_at datetime not null, updated_at datetime, unique (name, student_name) );* 「SQLエディタ」欄には,SQL プログラムを記述できる.
- 出力欄の確認
「出力」欄にエラーメッセージが表示されていないことを確認する.
SQL を用いたテーブルへの行の挿入
次のような scores テーブルを作成する.
以下の手順で,SQL を用いて scores テーブルへの行の挿入を行う.
- SQL プログラムの記述
「insert into ...」は行の挿入を意味する.ここには 5つの SQL 文を書き, 「begin transaction」と「commit」で囲む.
begin transaction; insert into scores values( 1, 'Database', 'K', 'KK', 85, datetime('now', 'localtime'), NULL ); insert into scores values( 2, 'Database', 'K', 'AA', 75, datetime('now', 'localtime'), NULL ); insert into scores values( 3, 'Database', 'K', 'LL', 90, datetime('now', 'localtime'), NULL ); insert into scores values( 4, 'Programming', 'A', 'KK', 85, datetime('now', 'localtime'), NULL ); insert into scores values( 5, 'Programming', 'A', 'LL', 75, datetime('now', 'localtime'), NULL ); commit;
- 複数の SQL 文の一括実行
「SQLエディタ」に記述した複数の SQL 文をまとめて実行するには,「▶ SQL実行 (F5)」を押下する.
- 「出力」欄の確認
エラーメッセージが出ていないことを確認する.
SQLite Manager Tool を用いたデータのブラウズ
- scores テーブル
まず,「スキーマナビゲータ」の一覧から scores テーブルを選択する.
「テーブル定義ビューワ」に scores テーブルの CREATE 文が,「出力」欄にデータが表示される.
* データに間違いがあれば,このウインドウで修正できる.
DISTINCT を含む SQL の例
DISTINCT を付ける場合
SELECT DISTINCT name, teacher_name
FROM scores;
DISTINCT を付けない場合
SELECT name, teacher_name
FROM scores;
DISTINCT を付ける場合
SELECT DISTINCT score
FROM scores;
DISTINCT を付けない場合
SELECT score
FROM scores;
SQL を用いたテーブルの分解
scores(name, teacher_name, student_name, score) を 2つのテーブルに分解する:
- A(name, teacher_name)
- B(id, name, student_name, score)
テーブルを分解する際には,重複行を除去するという規則があることに注意する.DISTINCTを使用する.
SQL の実行
create table A AS
select distinct name, teacher_name
FROM scores;
データのブラウズ機能を使いテーブル A を確認する.
SQL の実行
create table B AS
select distinct id, name, student_name, score
FROM scores;
データのブラウズ機能を使いテーブル B を確認する.
結合問い合わせ (join query)
元のテーブル scores が,分解後の2つのテーブル A, B から復元できることを確認する. テーブル scores は,テーブル A と B から復元できるため,データベース設計として:
- テーブル scores をデータベースに格納する方法
- テーブル scores ではなく,テーブル A と B をデータベースに格納する方法
という 2つの選択肢が考えられる.(では,どちらが良いのか? 今度の授業で明らかにしていく)
Examine that the original table 'scores' can be constructed from the two tables A and B. It means that there are two alternatives in database design.
- Store 'scores' into database, or
- Store 'A' and 'B' instead of 'scores' in database.
SELECT B.id, A.name, A.teacher_name, B.student_name, B.score
FROM A, B
WHERE A.name = B.name;
情報無損失分解にならない分解
今度は,テーブルの分解の仕方を変える. 分解後のテーブルから,もとのテーブルに復元できない場合があることを確認する.
Table decompositions are not always LOSSLESS.
SQL の実行
create table C AS
select distinct name, student_name
FROM scores;
データのブラウズ
SQL の実行
create table D AS
select distinct id, teacher_name, student_name, score
FROM scores;
データのブラウズ
テーブル C, D からは,テーブル scores を構築できない.
SELECT D.id, C.name, D.teacher_name, C.student_name, D.score
FROM C, D
WHERE C.student_name = D.student_name;
演習問題と解答例
次の問いに答えよ.その後,下記の解答例を確認せよ.
問い
- 次の PTABLE テーブルに関する問題
name | type | color ------------------------------ apple | fruit | red apple | fruit | blue rose | flower | white rose | flower | red rose | flower | yellow- 次の SQL の評価結果は何か?
SELECT DISTINCT name, type FROM PTABLE; - 次の SQL の評価結果は何か?
SELECT DISTINCT name FROM PTABLE;
- 次の SQL の評価結果は何か?
- 下記に関する問題
次のようなテーブル R(id, price, type) を作成したい.
id|price|type ------------------------------ 1 |10 |author 2 |21 |name 3 |30 |author 4 |45 |name 5 |50 |authorそこで,SQLite 3 で 次の SQL を実行した.
create table R ( id integer primary key autoincrement not null, price integer not null, type text ); insert into R(price, type) values(10, 'author'); insert into R(price, type) values(21, 'name'); insert into R(price, type) values(30, 'author'); insert into R(price, type) values(45, 'name'); insert into R(price, type) values(50, 'author');- 次の SQL の評価結果は何か?
SELECT DISTINCT type FROM R; - 次のようなテーブル S(tid, type) を作成したい.
tid | type ----------- 1 |author 2 |nameそのために,次のSQLを評価する.
create table S ( tid integer primary key autoincrement not null, type text); insert into S(tid, type) values(1, 'author'); insert into S(tid, type) values(2, 'name'); - 次の SQL の評価結果は何か?
SELECT R.id, R.price, R.type, S.tid FROM R, S where R.type = S.type;
- 次の SQL の評価結果は何か?
- 次のSQLを評価する.「SELECT * FROM T2;」の評価結果は何か?
CREATE TEMPORARY TABLE T1 AS select distinct type FROM R where type IS not null; create table T2 AS SELECT OID AS typeid, * FROM T1; SELECT * FROM T2; - RとSを使って,テーブル RR(id, price, tid) を作成したい.
そこで,次のSQLを評価する.「SELECT * FROM RR;」の評価結果は何か?
create table RR AS SELECT R.id AS id, R.price AS price, S.tid AS tid FROM R NATURAL JOIN S; SELECT * FROM RR;
解答例
* 問い合わせ結果は1つのテーブルになる.その属性名には,元のテーブル名と属性名をドットでつなげたドット記法を用いている.
- 問1
-
select distinct name, type FROM PTABLE; name | type ------------------ apple | fruit rose | flower -
select distinct name FROM PTABLE; name -------- apple rose
-
- 問2
-
select distinct type FROM R; type -------- author name -
SELECT R.id, R.price, R.type, S.tid FROM R, S where R.type = S.type; id | price | type | tid ----------------------------- 1 | 10 | author | 1 2 | 21 | name | 2 3 | 30 | author | 1 4 | 45 | name | 2 5 | 50 | author | 1
-
- 問3
SELECT * FROM T2; 1|author 2|name - 問4
SELECT * FROM RR; id|price|tid 1|10|1 2|21|2 3|30|1 4|45|2 5|50|1