第十章

SQL命令の種類

データベースを使う2つの立場

立場① データベースにデータの出し入れを指示する立場

立場② 立場①の人が、効率よく、安全にデータの出し入れができるよう必要なテーブル準備や各種設定を指示する立場

3種類の命令

立場②としてテーブル作成などを指示する場合にも、SQLを使います。CREATE TABLEという命令を使います。立場②として使う命令はほかにもたくさん準備されていますが、すべてのSQL文は、最終的に次の3種類の命令に分類することができます。

・データ操作言語(DML)-立場①
データの格納や取り出し、削除などを行うための命令
・データ定義言語(DDL)-立場②
データを格納するテーブルなどの作成や削除、各種設定を行うための命令
・データ制御言語(DCL)-立場②
DMLやDDLの利用に関する許可や禁止を設定するための命令

DCLとは

DCLは、誰に、どのようなデータ操作やテーブル操作を許すかといった権限を設定するためのSQL命令の総称です。権限を付与するGRANT文と剥奪するREVOKE文があります。

GRANT文とREVOKE文

GRANT 権限名 TO ユーザー名

REVOKE 権限名 FROM ユーザー名

これらは、立場②の中でも特にデータベースの全権を管理する、データベース管理者の立場の人だけが使う命令です。

テーブルの作成

テーブル作成の基本

テーブルを作成するには、CREATE TABLE文を使います。作成したいテーブルの名前、テーブルを構成する列と型の一覧を指定し、テーブルを定義します。

テーブルの作成(基本形)

CREATE TABLE テーブル名(
  列名1 列1の型名,
  列名2 列2の型名,
  :
  列名X  列Xの型名,

これまで利用してきた家計簿テーブルを作成するには、以下のようなSQL文を実行します。

家計簿テーブルを作成する

CREATE TABLE 家計簿(
  日付        DATE,
  費目ID      INTEGER,
  メモ        VARCHAR(100),
  入金額      INTEGER,
  出金額      INTEGER,

デフォルト値の指定

あるテーブルに対してINSERT文によって行が追加される際、一部の列の値が指定されないことがあります。例えば、家計簿テーブルに行を追加する以下のリストのように、「費目ID」や「入金額」が省略されるかもしれません。

家計簿テーブルに対する行の追加

INSERT INTO(日付, メモ, 出金額)
     VALUES('2013-04-12', '詳細は後で', 60000)

このSQL文が実行されると、テーブルに追加された行に「費目ID」と「入金額」の列の内容は、NULLになります。

テーブルを作成する際に、デフォルト値を決めておくことで、「特に指定しなければ入金額には0が格納される」というような設定を行うことが可能です。
そのためには、CREATE TABLE文にDEFAULTキーワードを指定します。

デフォルト値の指定を含むテーブルの作成

CREATE TABLE テーブル名(
  列名 型名 DEFAULT デフォルト値,
    :

データベース内に、同じ名前のテーブルを複数作ることはできないので、テーブルを作り直すには、テーブルをいったん削除しなければなりません。
テーブルそのものを削除するにはDROP TABLE文を利用します。

テーブルの削除

DROP TABLE テーブル名

テーブル定義の内容を変更するには、ALTER TABLE文を使います。この文では、具体的にテーブルの「何を」「どう」変えるかを指定する必要があります。

テーブル定義の変更
列の追加
ALTER TABLE テーブル名 ADD 列名 型 制約
列の削除
ALTER TABLE テーブル名 DROP 列名 型 制約

既存のテーブルに列を追加する場合、挿入される位置は、原則として一番最後になります。

制約

人為的ミスに備える

データベースの利用者が、文法としては正しいものの、システムの意図としては誤ったSQL文をDBMSに送ってしまうような人為的ミスに対してトランザクション制御はまったくの無力です。「誤った内容のSQL文」が、「指示通りに正しく実行」されてしまいます。
DBMSは、人為的ミスによる意図しないデータの格納が行われないための仕組みをいくつも備えています。「型」もそんな安全機構の1つです。

あえて制限することで安全性を高める

予期しない値を格納できないように制限をかけることで、
人為的ミスによるデータ破壊の可能性を減らすことができる。

多くのDBMSは制約という仕組みを備えており、型よりもさらに強力な制限をかけることができます。

基本的な3つの制約

制約は、CREATE TABLE文でテーブルを定義する際に、列定義の末尾に指定することが可能です。

CREATE TABLE文中における制約の指定

CREATE TABLE テーブル名(
  列名 型 制約の指定,
  :

制約を複数指定することもできますが、カンマで区切らずにそのまま並べて記述します。

【その1】NOT NULL制約

NOT NULL制約が設定された列には、NULLの格納は許可されません。
NOT NULL制約はDEFAULT指定と組み合わせて利用されることがほとんどです。デフォルト値が設定されていれば、INSERT文で特に値を入力しなくても自動的にその値が設定されるため、エラーにならないからです。

【その2】UNIQUE制約

ある列の内容が決して重複してはならない場合、UNIQUE制約を付けます。UNIQUE制約がかけられていても、NULLが格納された行が複数存在することは許されます。「NULLはNULLとも等しくない」からです。

【その3】CHECK制約

ある列に格納される値が妥当であるかを細かく判定したい場合は、CHECK制約を用います。CHECKの後ろのカッコ内に記述した条件式がTRUEとなるような値だけが格納を許されます。

主キー制約

主キーの役割を担う列には、主キー制約を付けましょう。この制約がついてる列は、単なる「NULLも重複も許されない列」ではなく、主キーとしての役割が期待されているという意味を持ちます。

主キーを付ける方法は2つあります。ある単独の列に指定したい場合は、ほかの制約と同様、列名の末尾に記述します。この記法を用いる場合、複数の列に主キー制約を指定することはできません。

主キー制約の指定(1)

 CREATE TABLE 費目 (
  ID  INTEGER    PRIMARY KEY,
  名前 VARCHAR(40)  UNIQUE
)

CREATE TABLE文の最後に記述する記法を用いれば複合キーの指定も可能です。

主キー制約の指定(2)

 CREATE TABLE 費目 (
  ID  INTEGER,
  名前 VARCHAR(40)  UNIQUE,
  PRIMARY KEY(ID, 名前)
)

主キーの列に関しては、万が一にもNULLや重複値が格納されると行が識別できないという致命的な状況に陥るため、特別な理由がない限り、主キー制約を指定するようにしましょう。

外部キーと参照整合性

参照整合性の崩壊

外部キーが指し示す先にきちんと行が存在してリレーションが成立していることを参照整合性といいます。外部キーで別テーブルの行を参照しているのに、その行が存在しないような、異常な状態になってしまうことは「参照整合性の崩壊」といわれ、データベース利用において絶対に避けなければならないことです。

参照整合性を引き起こすデータ操作には、次に挙げる4つのパターンがあります。

参照整合性の崩壊を引き起こすデータ操作
  1. 「ほかの行から参照されている」行を削除してしまう。
  2. 「ほかの行から参照されている」行の主キーを変更してしまう。
  3. 「存在しない行を参照する」行を追加してしまう。
  4. 「存在しない行を参照する」行に更新してしまう。

外部キー制約

参照整合性が崩れるようなデータ操作をしようとした場合にエラーを発生させ、強制的に処理を中断させる制約が外部キー制約です。この制約は、参照元のテーブルの外部キー列に設定します。

CREATE TABLE文で外部キー制約をかけるには、次のような構文を利用します。

外部キー制約の指定(1)

CREATE TABLE テーブル名 (
  列名 型 REFERENCES 参照先テーブル名 (参照先列名)
  :
)

主キーの場合と同様に、CREATE TABLE文の最後にまとめて定義することも可能です。この場合は、「FOREIGN KEY」で制約を付ける列を指定します。

外部キー制約の指定(2)

CREATE TABLE テーブル名 (
  :
  FOREIGN KEY (参照元列名) REFERENCES 参照先テーブル名 (参照先列名)
)


s

第十一章

データベースをより速くする

検索を速くする方法

あるページを検索する場合、記憶をたどりながらページをめくったり、最初のページからしらみつぶしに探していくなど、様々な方法が考えられます。しかし、最も効率がよいのは巻末の「索引」を使って検索することではないでしょうか。データベース内のテーブルに対しても、書籍の索引と似たものを作ることができます。

データベースで作成することのできる索引情報はインデックスと呼ばれ、次のような特徴があります。

インデックスの特徴

インデックスを作成するには、DDLに属する命令であるCREATE INDEX文を使います。

インデックスの作成

CREATE INDEX インデックス名 ON テーブル名 (列名)

インデックス名は、他と重複しない範囲で好きな名前を付けることができます。この名前は、DROP INDEX文でインデックスを削除するときにも使います。

インデックスの削除

DROP INDEX インデックス名
DROP INDEX インデックス名 ON テーブル名--MySQLの場合

複数の列を1つのインデックスとする複合インデックスも作成可能です。

高速化のパターン

一般的にインデックス設定の効果が高い列
WHERE句に頻繁に登場する列
完全一致検索では、インデックスが使用され、高速に検索結果を得ることができます。
ORDER BY句に頻繁に登場する列
インデックスには並び替えを高速に行えるようにする効果もあるため、処理が速くなります。
JOINの結合条件に頻繁に登場する列(外部キーの列)
結合処理は内部で並び替えを行っているため、インデックスのある列を使うと高速になります。

*実際にどのような検索にインデックスが利用されるかは、DBMSの種類やDBMSが採用するインデックスのアルゴリズムに依存する。

インデックスの注意点

インデックスを作成することによるデメリット

特に重要なのが2つ目のデメリットです。インデックスが作成されている列のデータを変更する場合、DBMSはそのたびにインデックス情報を更新する必要があり、更新処理に時間がかかるようになってしまうのです。

ビュー

データベースを利用していると、同じようなSQL文を頻繁に実行していることに気付く場合があります。例えば、「4月のすべての入出金を表示する」および「4月に使った費目を一覧表示する」には、以下のSQL文を実行します。

4月の家計簿に関する様々なSQL文の実行

SELECT * FROM 家計簿
 WHERE 日付 >= '2013-04-01'
  AND 日付 <= '2013-04-30';
SELECT DISTINCT 費目 ID FROM 家計簿
 WHERE 日付 >= '2013-04-01'
  AND 日付 <= '2013-04-30';

2つのSELECT文に同一のWHERE句が記述されています。4月についての検索を行うたび同じSQL文を書くのは面倒です。このような場合に便利なのが、結果表をテーブルのように扱えるビューという機能です。例えば、「SELECT文を使って家計簿テーブルから4月の分だけを抽出したもの」を「家計簿4月」ビューとして作成し、それをテーブルの様に利用することができます。

ビューの作成にはCREATE VIEW文を、削除にはDROP VIEW文を使います。

ビューの作成と削除

CREATE VIEW ビュー名 AS SELECT文
DROP VIEW ビュー名

ビューを使うことにより、SQL文がシンプルになります。

ビューにはもう1つメリットがあります。テーブルAのある列に機密情報が含まれており、一般の利用者にはその列を見せたくない状況があるとします。そのような場合、機密情報の列だけを除いたビューBを定義しておきます。GRANT文を使って、一般の利用者に対して「テーブルAはアクセス禁止、ビューBは許可」という設定をすることにより、データ参照を許可する範囲を利用者の立場に応じて適切に定めることができます。

ビューの制約とデメリット

テーブルとビューはまったく同じというわけではありません。テーブルに対しては自由にINSERTやUPDATEを行うことができますが、ビューに対してはいくつかの条件が揃わなければSELECTしか行うことができません。ビューの実体は単なる「名前を付けたSELECT文」でしかありません。
ビューを展開してしまうと、送信したSQL文は非常にシンプルであるのに対し、実際に実行されるSQL文は非常に複雑なものになってしまい、一見するよりも負荷の高い処理になる可能性があります。

採番の方法

あるテーブルに行を追加する場合、主キーの値を何にすべきか迷うことがあります。主キーである以上、すでに使われている値と重複することは許されませんので、連番を振るという方法がよく用いられます。追加する行に独自の番号振るために、適切な番号を取得することを採番とも言います。
開発現場では、すでに採番した番号や最後に採番した番号を、専用のテーブルに記憶しておくなどの手法が使われます。この記録用のテーブルは採番テーブルと呼ばれ、工夫次第では記号や数字が混じった独自の番号も重複することなく採番することが可能です。

(1)連番が自動的に振られる特殊な列を定義できる
SQL Server、MySQL、SQLite、PostgreSQLの場合、CREATE TABLE文で列を定義する際に「連番を振る列である」ことを指定するだけで、データが追加されるタイミングで自動的に連番が振られるようになります。

各DBMS製品の連番用修飾
DBMS連番列を定義する方法
SQL ServerIDENTITY 修飾を付与
MySQLAUTO_INCREMENT 修飾を付与
PostgreSQLデータ型にSERIAL型を利用
SQLiteAUTOINCREMENT 修飾を付与

(2)連番を管理してくれる専用の道具が提供されている
Oracle DB、DB2、SQL server、PostgreSQLでは、専用の道具としてシーケンスが利用できます。シーケンスは常に採番した最新の値を記憶しており、シーケンスに指示をすることで「現在の値」や「次の値」を取り出すことができます。シーケンスから値を取り出すと、その操作はすぐに確定し、トランザクションをロールバックしてもシーケンスの値は戻りません。

シーケンスは、CREATE SEQUENCE文で作成し、DROP SEQUENCE文で削除することができます。

シーケンス作成と削除

CREATE SEQUENCE シーケンス名
DROP SEQUENCE シーケンス名

*CREATE SEQUENCE文には、「デフォルト値」や「一回の採番で増やす値」「最大値」などのオプションが指定可能。

シーケンスから値を取り出す方法はDBMS製品によって大きく異なります。Oracle DBやDB2では、シーケンスを疑似的テーブルとみなし、SELECT文を使うことで値を取り出したり、次の値に進めたりできます。
Oracle DBでは、シーケンスを作成した直後にCURRVALで現在の値を取得することはできません。現在の値を知るには、必ず、NEXTVALでシーケンスを次の値に進めておく必要があります。

PostgreSQLでは、SELECT文ではなく関数を使ってアクセスします。

INSERT文中の副問い合わせとして記述すれば、シーケンス値の採番と同時にデータを追加することができます。

データベースをより安全に使う

信頼性のために備えるべき4つの特性

ITの世界では、「データを正確かつ安全に取り扱うためのシステムが備えるべき4つの特性」として、ACID特性というものが広く知られています。ACIDとは、これまでに学んできた中の原子性、一貫性、分離性と永続性を言います。

バックアップの仕組み

多くのDBMSは、万が一のデータ消失に備えてバックアップの仕組みを備えています。それは、全内容をファイルに出力することができる、というものです。バックアップは毎日や毎週など定期的な感覚で自動的に行われるように設定されます。

バックアップの整合性

整合性を保ちつつバックアップを行うもっとも簡単な方法は、データベースを停止してからバックアップを行うオフラインバックアップです。しかし、オフラインバックアップ中は一切のデータ処理が行えなくなります。データベースやそれを使ったシステムが停止してしまうことは状況によっては許されないかもしれません。
そのため、多くのDBMSは、稼働しながら整合性のあるバックアップデータを取得できるオンラインバックアップ機能も備えています。

ログファイルのバックアップ

バックアップは時間がかかる処理なので、あまり頻繁に行うわけにはいきません。しかし、バックアップを1日1回だけにしてしまうと、それまでにディスクが壊れてしまった場合、それまでの処理結果がすべて失われてしまいます。
このようなことにならないように、通常のバックアップなどは低い頻度で行う代わりに、データベースが出力するログファイルを10分周期や1時間周期といった高頻度でバックアップするのです。
データベースのログは、REDOログまたはトランザクションログなどと呼ばれ、その内容は「それまでに実行したすべてのSQL文」にほかなりません。ログファイルを高い頻度でバックアップしておくと、データ消失時にも以下のような手順を踏むことで、消失直前の時点までデータを復元することができます。

バックアップからの復元方法

ログに記録されているSQL文を再実行して、障害が発生する直前の状態までデータを更新する処理のことをロールフォワードと言います。

第十二章

システム化と要件

データベースを用いたシステムを開発するには

SQLやDBMSの機能に関する知識だけでは、データベースを用いたシステムは開発できない。要件をしっかりと理解し、その要件をデータベース設計に適切に落としむための方法論を活用しなければならない。

データベース設計の流れ

システム開発の一環としてデータベースを作ろうとする場合、使える材料(INPUT)と、作るべきもの(OUTPUT)を明確にすることが大切です。

データベース構築のINPUTとOUTPUT
①お客様の要件聴取を行います。(INPUT)
お客様から要件を聞き出すことは非常に大切な作業です。
②データベース設計作業
概念設計、論理設計、物理設計を行っていく。
③DDL(OUTPUT)
必要なテーブルを持つデータベースを構築。

データベース設計作業

概念設計
管理すべき情報はどのようなものなのかを整理します。データベースやシステムに関することは考えず、要件に登場する情報だけをザックリと把握します。
論理設計
概念設計で明らかになった各情報について、RDBを使う前提で構造を整理し詳しく具体化していきます。
物理設計
特定のDBMS製品を使う前提に立ち、論理設計で明らかになった各テーブルについて、その内容を詳しく具体化していきます。

概念設計

概念設計ですること

概念設計では、要件を実現するために、抽象的な概念としてどのような「情報の塊」を管理しなければならないかを明らかにします。この情報の塊のことをエンティティといい、通常エンティティは複数の属性を持っています。

概念的なもののイメージをつかむためのヒント

エンティティ ・・・「テーブル」のようなもの
属性     ・・・テーブルの「列」のようなもの
関係     ・・・「リレーション」のようなもの

概念設計の成果は、ER図という図にまとめることが一般的です。ER図を使うことで、エンティティ、属性、リレーションをふかんして見ることができます。

ER図

ER図には、IEという形式と、IDEF1Xという2つの記述形式があります。

ER図に登場する四角形はエンティティを表しています。四角形の上にはエンティティの名前が、中には属性の一覧が記述されます。属性の一覧は、2つのグループに分けられます。四角形の中の線より上には、エンティティを一意に特定する主キーとなる属性を記述します。複数の属性で複合キーを構成する場合、線より上に複数の属性が記述されます。複数のエンティティ間にリレーションがある場合には、エンティティ同士を線で繋ぎ、外部キーとなる属性には「FK」と付記します。エンティティ同士の数量的な関係を多重度カーディナリティと言います。

IEによる関連の書き方

エンティティを導き出す方法

【ステップ1】候補となる用語を洗い出す

【ステップ2】不要な用語を捨てる

【ステップ3】関連がありそうなものをまとめる

【ステップ4】

ER図では、エンティティの中にエンティティを作ることはできません。別のエンティティとして、外部に取り出すようにしましょう。外部に取り出したエンティティは、元のエンティティと関係があるはずです。元のエンティティと関連付けれるように、取り出したエンティティに元のエンティティの主キーを属性として追加しておきます。

論理設計

論理設計ですること

論理設計の目的

概念上のエンティティをリレーショナルデータモデルで取扱いやすい形のテーブルに変形する。

どのような変形を行えばいいか、論理設計の流れを確認します。

「多対多」の分解

リレーショナルデータベースは「多対多」の関係をうまく扱うことができません。2つのエンティティの対応を格納した中間テーブルを追加することによって、「多対多」を2つの「1対多」の関係に変換します。

キーの整理

出揃ったすべてのエンティティのキーについて整理と確認を行います。特に重要なのは主キーです。主キーを持たないエンティティには、管理をしやすくするために人工的な主キーを追加します。

正規化

論理設計におけるもっとも中心的な作業は、正規化の作業です。正規化とは、矛盾したデータを格納できないよう、テーブルを複数に分割していく作業です。正規化という手法を用いて正しくテーブルを分割することにより、ヒューマンエラーを防止することができます。

正規化の手順

正規化の段階

正規形は第1正規形から第5正規形まで存在します。通常のシステム開発が目的の場合は、業務で求められる第3正規形までしていれば問題ないでしょう。

第1正規形への変形

第1正規形の目指す姿と達成条件

テーブルのすべての行のすべての列に1つずつ値が入っているべきである。
よって、繰り返しのセルやセル結合が現れてはならない。

【ステップ1】繰り返しの列の部分を別の表に切り出す
元のテーブルから「繰り返しの列」の部分を別テーブルとして切り出し、切り出したテーブルに名前を付けます。
【ステップ2】切り出したテーブルの仮の主キーを決める
ステップ1で切り出したテーブルの主キーとなる列を決めます。
【ステップ3】主キー列をコピーして複合主キーを構成する
元のテーブルの主キー列を、切り出したテーブルにも加え、ステップ2の仮の主キーとあわせて複合主キーを構成します。

(ある列Aの値が決まれば、自ずと列Bの値も決まる)という関係。このとき、列Bは列Aに関数従属している。このことを関数従属性という。

テーブルに含まれる主キー以外の列は、主キーの列に対して関数従属しているべきなのです。テーブルの列が何らかの理由で「主キーに汚く関数従属してしまう」ことがあります。これを排除することこそ、第2正規形の目的です。

第2正規形への変形

第2正規形の目指す姿と達成条件

複合キーを持つテーブルの場合、非キー列は、複合主キーの全体に関数従属するべきである。よって、「複合主キーの一部の列に対してのみ関数従属する列」が含まれてはならない。

*複合主キーの一部の列にしか関数従属しない状態を、部分関数従属と言います。

部分関数従属を排除し、第2正規従属に変形するには、次の順番を実施します。

【ステップ1】複合主キーの一部に関数従属する列を切り出す
複合主キーの一部の列に関数従属している列を、別のテーブルとして切り出して名前を付けます。
【ステップ2】部分関数従属先だった列をコピーする
切り出した列が関数従属していた列を、ステップ1で作ったテーブルにコピーして主キーとします。

第3正規形への変形

第3正規形の目指す姿と達成条件

テーブルの非キー列は、主キーに直接、関数従属すべきである。
よって、「主キーに関数従属する列にさらに関数従属する列」は存在してはならない。

*間接的に関数従属することを、推移関数従属と言います。

次のような手順を踏んで、推移関数従属を排除します。

【ステップ1】間接的に主キーに関数従属している列を切り出す。
間接的に主キーに関数従属している列を、別のテーブルとして切り出して名前を付けます。
【ステップ2】直接関数従属先だった列をコピーする
切り出した列が関数従属していた列を、切り出したテーブルにコピーして主キーとします。
3つの正規化で排除しようとするもの

第1正規形への変形:繰り返し列
第2正規形への変形:複合主キーの一部への関数従属
第3正規形への変化:間接的な関数従属

物理設計

家計管理データベースの物理設計

  1. 最終的なテーブル名、列名(物理名)を決定する
    最終的にデータベース内にテーブルとして作成する場合、アルファベットの名前を付けることが多いです。
  2. 列の型を決定する
    各列に対して指定する型を決定します。
  3. 制約、デフォルト値を決定する
    各テーブルや各列に対して、設定する制約を決定します。
  4. インデックスを決定する
    どの列にインデックスを設定するのかについても、物理設計で決定する事柄です。
  5. その他
    利便性を考慮してビューを作成したり、性能のためにあえて正規化を崩したり、巨大なテーブルを分割したりする作業が行われることもあります。
管理に適した形、利用に適した形

管理するときは・・・データは複数のテーブルに分割してある方がよい。
利用するときは・・・データは1つのテーブルに結合してある方がよい。

人間にとってはあまり正規化されていない情報の方が取扱いやすいので、結合をたくさん使うのは、ある程度仕方ないことと言えます。