テーブルの分解 (table decomposition)
大学で使用した自作の資料等を,手直しの上公開している. クリエイティブ・コモンズ BY NC SA.
リレーショナルデータベースの基礎であるテーブル定義,一貫性制約,SQL,結合と分解,トランザクション,埋め込みSQL,実行計画,二次索引を学ぶ.SQLite 3 を用いて,SQL についての演習も行う.
【サイト内の関連ページ】
- テーブル(table)
リレーショナルデータベース での「テーブル」は,多重集合 (muti 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, student_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」のドメインの直積集合
- べき集合の例: ドメイン {みかん,りんご,バナナ}から構成されるべき集合は,{φ, {みかん}, {りんご}, {バナナ}, {みかん, りんご}, {りんご, バナナ}, {バナナ, みかん}, {みかん, りんご, バナナ}}
演習
演習で行うこと
- Sqliteman の起動と終了 (Start Sqliteman)
- Sqliteman で既存のデータベースを開く (Open an existing database using Sqliteman)
- SQL を用いたテーブル定義と一貫性制約の記述 (Table defintion and integrity constraint specification using 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 を用いたテーブルへの行の挿入 (Insert rows into a table using 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形式.
- Sqliteman を用いたデータのブラウズ (Browse Data using Sqliteman)
- DISTINCT を含む SQL の例 (SQL with DISTINCT)
SELECT DISTINCT name, teacher_name FROM scores; SELECT name, teacher_name FROM scores; SELECT DISTINCT score FROM scores; SELECT score FROM scores;
- SQL を用いたテーブルの分解 (table decomposition using 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;
- 情報無損失分解にならない分解 (Inspect lossless decomposition)
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 3 の SQL 演習に関連する部分
SQLite 3の SQL の説明は http://www.hwaci.com/sw/sqlite/lang.html (English Web Page) にある.
- select distinct ...
同一の値を持つ行が複数あるとき,1つを残して除去する
- create table AS <table-name> SELECT ...
SQL 問い合わせ結果から新しいテーブルを作る
Sqliteman の起動と終了 (Start Sqliteman)
- Sqliteman の起動 (Start Sqliteman)
* Ubuntu での SQLiteman の起動例
「プログラミング」→「Sqliteman」と操作する.
SQLiteman の新しいウインドウが開く.(A New window appears)
* Lubuntu での SQLiteman の起動例
「プログラミング」→「Sqliteman」と操作する
SQLiteman の新しいウインドウが開く.(A New window appears)
* Windows での SQLiteman の起動例
「Sqliteman」 のアイコンをダブルクリック (double click "Sqliteman.exe")
Sqliteman のウインドウが開く.(A New window appears)
SQLiteman で新しいデータベースを作成する (Create a new database)
前回の授業で作成したデータベースを使うときは、ここの手順は行わなくて良い
以下の手順で,新しいデータベースを作成する.その結果,データベースファイルができる. (Create a new database)
- 「File」→ 「New」
- データベースの新規作成を開始する (Start a new databae creation)
データベースファイル名 を指定
Sqliteman で既存のデータベースを開く (Open an existing database using Sqliteman)
すでに作成済みのデータベースを,下記の手順で開くことができる.
以下の手順で,既存のデータベースファイルを開く. (Open an existing database file)
- 「File」→
「Open」
- データベースファイルを開く (Open Database File)
* Ubuntu での実行例(「mydb」を開く場合)
データベースファイル mydb を選び, 「開く」をクリック (Click '開く' after choosing the database file "mydb")
* Windows での実行例(「C:\SQLite\mydb」を開く場合)
データベースファイル C:\SQLite\mydb を選び, 「開く」をクリック (Click '開く' after choosing the database file "C:\SQLite\mydb")
要するに,/home/<ユーザ名>/SQLite 3の下の mydb を選ぶ.
SQL を用いたテーブル定義と一貫性制約の記述 (Table definition and integrity constraint specification using SQL)
SQL を用いて,scores テーブルを定義し,一貫性制約を記述する. (Define 'scores' table and specify integrity constrants of the table using SQL)
リレーショナル・スキーマ (relational schema): scores(id, name, teacher_name, student_name, score, created_at, updated_at)
- scores テーブルの定義 (Define a table)
次の SQL を入力し,「Run SQL」のアイコンをクリック (Write the following SQL, and click "Run SQL" icon).
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 Editor」のウインドウには,SQL プログラムを書くことができる. In the 'SQL string' window, you can write down SQL program(s).
- コンソールの確認 (Inspect console)
エラーメッセージが出ていないことを確認
SQL を用いたテーブルへの行の挿入 (Insert rows into a table using SQL)
次のような scores テーブルを作る.(Construct table 'scores)
以下の手順で,SQL を用いて scores テーブルへの行の挿入を行う (Insert rows into table 'scores' using SQL)
- SQL プログラムの記述
「insert into ...」は行の挿入.ここには 5つの SQL 文を書き, 「begin transaction」と「commit」で囲む. ("insert into ..." means inserting a row into a table. Four SQL statements are wrote).
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 文の一括実行 (Run multiple SQL statements)
複数の SQL 文を一括実行したいので,カーソルを先頭行に移動した後に,「Run multiple SQL statements ...」のボタンをクリックする. 「Move the cursor to the top statement. Click "Run multiple SQL statements from current cursor position in one batch" icon)
- 「Script Output」ウインドウの確認 (Inspect "Script Output" window)
エラーメッセージが出ていないことを確認
Sqliteman を用いたデータのブラウズ (Browse Data using Sqliteman)
- scores テーブル
まず,オブジェクト・ブラウザ (Object Browser) の中の「Tables」を展開 (Click 'Tables')
次に,テーブル scoresを選ぶ (Select table 'scores'). テーブル scoresが表示される (table 'scores' appears)
* もし,データに間違いがあれば,このウインドウで修正できる (If you find any mistakes, you can modify the data using this window).
DISTINCT を含む SQL の例 (SQL with DISTINCT)
DISTINCT を付ける (with DISTINCT)
SELECT DISTINCT name, teacher_name FROM scores;
DISTINCT を付けない (without DISTINCT)
SELECT name, teacher_name FROM scores;
DISTINCT を付ける (with DISTINCT)
SELECT DISTINCT score FROM scores;
DISTINCT を付けない (without DISTINCT)
SELECT score FROM scores;
SQL を用いたテーブルの分解 (table decomposition using SQL)
scores( name, teacher_name, student_name, score )を 2つのテーブル
- A(name, teacher_name)
- B(name, student_score, score)
に分解.テーブルを分解するときは重複行を除去するという規則があることに注意.DISTINCTを使う.
'scores' table is decomposed into two tables A and B. Duplicate rows in A and B are eliminated when decomposition (because of the definition of "table decomposition"). Use 'DISTINCT'.
SQL の実行 (Execute SQL)
create table A AS select distinct name, teacher_name FROM scores;
データのブラウズ機能を使いテーブル A を確認 (Browse Table A)
SQL の実行 (Execute SQL)
create table B AS select distinct id, name, student_name, score FROM scores;
データのブラウズ機能を使いテーブル B を確認 (Browse Table 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;
情報無損失分解にならない分解 (Inspect lossless decomposition)
今度は,テーブルの分解の仕方を変える. 分解後のテーブルから,もとのテーブルに戻らない場合がある. このことを確認しておく.
Table decompositions are not always LOSSLESS.
SQL の実行 (Execute SQL)
create table C AS select distinct name, student_name FROM scores;
データのブラウズ (Browse Data)
SQL の実行 (Execute SQL)
create table D AS select distinct id, teacher_name, student_name, score FROM scores;
データのブラウズ (Browse Data)
テーブル C, D からは,テーブル scores を構築できない ('scores' table can not be constructed from C and D).
SELECT D.id, C.name, D.teacher_name, C.student_name, D.score FROM C, D WHERE C.student_name = D.student_name;
演習問題と解答例
次の問いに答えよ.その後,下記の解答例を確認せよ. Answer the following questions. Then, inspect answers described below.
問い (Questions)
- 次の PTABLE テーブルに関する問題 (About the following 'PTABLE' table)
name | type | color ------------------------------ apple | fruit | red apple | fruit | blue rose | flower | white rose | flower | red rose | flower | yellow
- 次の SQL の評価結果は何か? What is the result of the following SQL ?
SELECT DISTINCT name, type FROM PTABLE;
- 次の SQL の評価結果は何か? What is the result of the following SQL ?
SELECT DISTINCT name FROM PTABLE;
- 次の SQL の評価結果は何か? What is the result of the following 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 を実行した.(To create table R, the following SQL commands are evaluated on SQLite 3)
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 の評価結果は何か? What is the result of the following SQL ?
SELECT DISTINCT type FROM R;
- 次のようなテーブル S(typeid, 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 の評価結果は何か? What is the result of the following SQL ?
SELECT R.id, R.price, R.type, S.tid FROM R, S where R.type = S.type;
- 次の SQL の評価結果は何か? What is the result of the following 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, typeid) を作りたい
そこで、次の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;
解答例 (Answers)
* 問い合わせ結果は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
-
SELECT * FROM T2; 1|author 2|name
-
SELECT * FROM RR; id|price|tid 1|10|1 2|21|2 3|30|1 4|45|2 5|50|1
-