大学で使用した自作の資料等を,手直しの上公開している. クリエイティブ・コモンズ BY NC SA.
リレーショナルデータベースの基礎であるテーブル定義,一貫性制約,SQL,結合と分解,トランザクション,埋め込みSQL,実行計画,二次索引を学ぶ.SQLite 3 を用いて,SQL についての演習も行う.
【サイト内のリレーショナルデータベース関連の資料】
索引を用いることで,レコードを効率よく検索することができる.
但し,レコードデータの更新時には,索引の維持 (maintenance) を行わなければならないので,索引を増やしすぎるのは良くない.
主キー (Key Primary) の値とレコードとを直接結びつけているような索引構造のことを,この資料では「主索引」と呼ぶことにする.
各テーブルに,1つの主索引がある(1つのテーブルに2個以上の主索引はありえない).
テーブル定義において「primary key」と指定した属性が,主キーになるのが普通である.
「primary key」を指定しなかったときは, テーブル定義が「primary key」を含まないなどの場合には,データベース管理システムが,テーブルの各行を識別する識別番号を自動生成し,それを主索引のキーとして使うのが普通である.
主索引のキー以外の属性による索引構造.
二次索引は,1つのテーブルに対して任意個作ることができる.二次索引は自由に追加,削除できるのが普通である.
【SQLite 3 の主要なオペコード (Opcode) の要点】
#define SQLITE_AFF_TEXT 'a' #define SQLITE_AFF_NONE 'b' #define SQLITE_AFF_NUMERIC 'c' #define SQLITE_AFF_integer 'd' #define SQLITE_AFF_real 'e'
※ SQLite 3 のオペコードの説明は http://www.hwaci.com/sw/sqlite/opcode.html にある.
※ SQLite 3の SQL の説明は http://www.hwaci.com/sw/sqlite/lang.html (English Web Page) にある.
CREATE INDEX <index-name> ON <table-name> ( <column-name の並び> )
テーブル名と属性名を指定して,二次索引を生成する.「index-name」とあるのは二次索引の名前のことで,後で二次索引を削除したいときなどに使う.
SQLite 3の SQL の説明は http://www.hwaci.com/sw/sqlite/lang.html (English Web Page) にある.
テーブル名と属性名を指定して,二次索引を生成する.「index-name」とあるのは二次索引の名前のことで,後で二次索引を削除したいときなどに使う.
索引名を指定して二次索引を削除する.
すでに作成済みのデータベースを,下記の手順で開くことができる.
以下の手順で,既存のデータベースファイルを開く. (Open an existing database file)
■ Ubuntu での実行例(「/home/ubuntuuser/mydb」を開く場合)
データベースファイル /home/ubuntuuser/mydb を選び, 「開く」をクリック (Click '開く' after choosing the database file "/home/ubuntuuser/mydb2")
■ Windows での実行例(「C:\SQLite\mydb」を開く場合)
データベースファイル C:\SQLite\mydb を選び, 「開く」をクリック (Click '開く' after choosing the database file "C:\SQLite\mydb")
要するに,/home/<ユーザ名>/SQLite 3の下の mydb を選ぶ.
SQL を用いて,point3 テーブルを定義し,一貫性制約を記述する. (Define 'point3' table and specify integrity constrants of the table using SQL)
リレーショナル・スキーマ (relational schema): point3(id, x, y, z, created_at)
次の SQL を入力し,「Run SQL」のアイコンをクリック (Write the following SQL, and click "Run SQL" icon).
create table point3 ( id integer primary key autoincrement not null, x real not null, y real not null, z real not null, created_at datetime not null );
※ 「SQL Editor」のウインドウには,SQL プログラムを書くことができる. In the 'SQL string' window, you can write down SQL program(s).
エラーメッセージが出ていないことを確認
以前の授業で定義した point3 テーブルを使う。
下記の操作により、演習用のデータ(1000行)を、point3 テーブルに格納する。
エラーメッセージが出ていないことを確認
下の図では、point3 テーブルのルートページ (root page) 番号は 12 になっている。 ルート・ページ番号は,SQLite 3 システムが決める値である。 ルート・ページ番号が 12 以外の値になっていても問題はない。
※ この資料では, point3 テーブルのルートページ (root page) は,12 になっているものとして、説明を続ける。
ここでは 条件を満足する行のみの表示する SQL のSQL 問い合わせ計画の表示例を示す.
データベース管理システムは, SQL 文をコンパイルし,SQL 問い合わせ計画を作る. SQL 問い合わせ計画とは,データベースに関する基本的なオペレータの並びである. (Database management system compliles a SQL statement into a SQL query plan. SQL query plan is a sequence or a tree of database operations ).
SQLite 3の問い合わせ計画については,SQLite Virtual Machine Opcodes の Web ページなどを見てください.(please refer to https://www.sqlite.org/opcode.html for SQLite opcodes)
SELECT * FROM point3 WHERE x < 1000000000;
※「1000000000」では、0 は 9個
SQL 文の前に「EXPLAIN」を付ける.(Add 'EXPLAIN' before a SQL statement)
EXPLAIN SELECT * FROM point3 WHERE x < 1000000000;
【表示された問い合わせ計画の要点】
アドレス (addr) | オペコード | 主なオペランド | |
1 | Integer | P1 = 1000000000, P2 = 1 | レジスタ1に,値1000000000をセットする (store 1000000000 into register #1) |
3 | OpenRead | P2 = 12 | ルート・ページが 12 であるようなテーブル (この場合は,テーブル point3) のカーソルを作る (Open table 'point3' for read, and make a cursor) |
4 | Rewind | P2 = 18 | カーソルを,テーブル point3 の先頭を指し示すようにする.テーブルが空の場合には,アドレス 18 (「Close」の行)にジャンプする (Use the first row. If the first row is empty then jump to '18') |
5 | Column | P2 = 1, P3 = 2 |
列番号1の値 (つまり x の値) を,レジスタ 2 に格納する.(Save #1 column value into the register #2).
|
7 | Ge | P1 = 1, P2 = 17, P3 = 2 | 条件付きジャンプ. レジスタ 1 の値とレジスタ 2 を比較.P1 の値 ≧ P2 の値のときに限り,アドレス 17 にジャンプする. (jump to 17 if and only if register #1 ≧ #2) |
8 から 15 | Columnなど | id, x, y, z, created_at の値を,それぞれ、レジスタ 4, 5, 6, 7, 8 に格納 | |
16 | ResultRow | P1 = 4, P2 = 5 | レジスタ 4 からレジスタ 8 までの値 (レジスタの個数は,P2 に指定した5個) を1行として出力する (Generate output using registers) |
17 | Next | P2 = 5 | もし,カーソルが指し示すレコードが末端レコードならば、次の命令に進む. もし,カーソルが指し示すレコードが末端レコードでなければ、カーソルを1つ進めて、アドレス5 (「Column 0 1 2」のところ) にジャンプする. (Advance cursor to the next tow. If there are more rows, then jump to the address '5') |
CREATE INDEX idx1 ON point3( x );
「idx1は索引名である.索引の管理(索引の削除など)に使用される. ('idx1' is index name).
索引は数秒以内で生成される.(The secondary index will be generated in a several seconds)
エラーメッセージが出ていないことを確認
下の図のように、テーブルpoint3の「indexes」を展開して、idx1 が出来ていることを確認する。
■ データベースの構造の確認 (Database Structure)
(Inspect the root page number of the secondary index 'idx1'. The number is automatically decided by the database management system)
■ SQL 問い合わせ計画の表示 (SQL query plan)
EXPLAIN SELECT * FROM point3 WHERE x < 1000000000;
【問い合わせ計画の要点】
上の図から、二次索引が使われていることが確認できる。
二次索引は事前に作成済み。二次索引は多数の索引エントリから構成され、個々の索引エントリは、元のテーブルの各行に対応する。 最初,カーソル番号1のカーソルは二次索引のルートノードにセットされる.(ルートノードとは、個々の二次索引の管理情報を含むノードである)。 次に、「x < 1000000000」という条件式を満たす最小の x を含む行の 索引エントリが、二次索引の中から検索される。この処理は高速である。カーソル番号1のカーソルは、いま検索された索引エントリを指し示すように動く。 その後、カーソル番号1のカーソルを使って、 テーブル point3 の行を取り出しながら、 カーソル番号1のカーソルが,二次索引の中だけを動く. 「x < 1000000000」という条件式を満たすすべての行が取り出し終えたら、処理を終える。
二次索引がないとき,テーブルの本体が1行ずつ処理される.テーブルの全ての行について処理が繰り返される. (このことを「tuple at a time」ともいう)。
二次索引を使うとき、「テーブルの本体が1行ずつ処理される」わけではない。このことで、データベース処理がより高速になることが期待できる。
【表示された問い合わせ計画の要点】
アドレス (addr) | オペコード | 主なオペランド | |
1 | Integer | P1 = 1000000000, P2 = 1 | レジスタ1に,値1000000000をセットする (store 1000000000 into register #1) |
3 | OpenRead | P1 = 0, P2 = 12 | ルート・ページが 12 であるようなテーブル (この場合は,テーブル point3) のカーソルを作る. カーソル番号は0 (Open table 'point3' for read, and make a cursor) |
4 | OpenRead | P1 = 0, P2 = 46 | ルート・ページが 46 であるような二次索引 (この場合は idx1 ) のカーソルを別に作る. カーソル番号は1 |
4 | Rewind | P1 = 1, P2 = 23 | P1 = 1 なので、カーソル番号1のカーソルを使う. カーソルを,二次索引 idx のルートノードを指し示すようにする.二次索引が空の場合には,アドレス 23 (「Close」の行)にジャンプする |
7 | IdxGE | P1 = 1, P2 = 23 | P1 = 1 なので、カーソル番号1のカーソルを使う. 検索キーの値(この場合は、レジスタ1に入っている「1000000000」)と、 カーソルが指し示している値を比較する。 もし、 「カーソルが指し示している値」 ≧ 「検索キーの値」のときは、アドレス 23 にジャンプする. |
11 から 12 | Seekなど | 二次索引を使って、カーソル0を動かす。 | |
14 から 20 | Columnなど | id, x, y, z, created_at の値を,それぞれ、レジスタ 4, 5, 6, 7, 8 に格納 | |
21 | ResultRow | P1 = 4, P2 = 5 | レジスタ 4 からレジスタ 8 までの値 (レジスタの個数は,P2 に指定した5個) を1行として出力する (Generate output using registers) |
22 | Next | P2 = 8 | もし,カーソルが指し示す二次索引ノードが末端ならば、次の命令に進む. もし,カーソルが指し示す二次索引ノードが末端でなければ、カーソルを1つ進めて、アドレス8 にジャンプする. |