# CHAPTER 3: 問い合わせ言語についてのトピックス

## 3.1 はじめに

この章では Empress 問い合わせ言語を使用上に関連した 一般的なトピックを紹介します。

- テーブルのジョイン
  テーブルジョインのために考慮すること - ジョインの最適化
- 検索とインデックス
  検索性能の最適化のためのインデックス作成の指標
- ビューの作成と使用
  データ分析と操作のためのビューの使用
- ビューの更新系操作
  更新可能なビュー定義の制約条件
- 分散データのビュー
  複数データベース上の複合ビューの作成
- 2次問い合わせ
  2次問い合わせを使用した複合ブール式の作成
- 日付計算
  日付の値に対し実行することが可能な日付演算子と有効な演算子
- 式の値からデータタイプへの変換
  出力値の変換および再フォーマットの例
- データベース特権
  デフォルト権限の決定
  特別なユーザークラス (DBA, CREATOR, USER, PUBLIC)
  特権クラス
- トランザクション
  トランザクションは、 テーブルへの取り返しのつかない影響なしにコマンドを実行でき、 その後、更新するためのコミットまたはロールバックが可能です。
- ロックとテーブルアクセス
  現在のテーブルへのアクセスを制御することにより、 アクセスの衝突を防ぎ、データの整合性を維持します。

## 3.2 テーブルのジョイン

リレーショナルデータベースでは、1度に複数のテーブルからデータを抽出することができます。 多くの場合、このような操作をジョインと呼び、 共通のアトリビュートを通じて互いに論理的に関連付けらたテーブルに対し、操作されます。 例えば、`personnel`と`loans`テーブルは、`name`アトリビュートを 通じて論理的に関連があります。 この意味は `name` を指定し、 このアトリビュートを使用する`loans`と`personnel`の テーブルの検索をすると従業員の個人番号と現在、未払いのローンの両方を 表示することができます。

また、論理的に関係がないテーブルのジョインも可能です。 例えば、`WHERE`句を使用せずに2つのテーブルから検索すると 最初のテーブルのすべてのレコードと 2番目のテーブルのすべてのレコードがジョインされます。 `WHERE`句を使用せずに2つのテーブルから検索する この検索結果の解釈は、ユーザーに任せられます。 このセクションでは、関係したアトリビュートを使用するテーブルジョインについて 記述します。

関連したアトリビュートのテーブルジョインを行う場合、 ジョインの条件は 等価演算子(=)で対象となるアトリビュートを比較することに よって設定され、この設定においてのアトリビュートは、 同じデータタイプでなければなりませんが、 この互いのアトリビュート名は、同じ名前である必要はありません。 つまり、 `personnel`テーブルの`name`アトリビュートは、 `loans`の`name`への論理的な関係に影響を与えないのであれば、 `employees`と言う名前であってもよいということになります。 同様に2つのアトリビュートに同一名が付いていても、 必ずしもその両方のテーブルのジョインが実行できるわけではありません。 （例えば、`personnel`テーブルの`name`と `customers`テーブルの`name`)

### 3.2.1 ジョインの設計

ジョイン操作の処理速度は検索の指定方法に依存するため、 それに対し注意を払わない場合、 ジョイン操作は多くの時間を消費する可能性があります。 このことは通常、特別に日常で使用する場合は問題にする 必要はありませんが、アプリケーションで使用する上での ジョインの設計をする場合は、この点は重要な問題となります。

ジョインでの`WHERE`句は、通常、 各テーブルから選択したレコードで限定した上で、 テーブルのレコードをリンクします。 `repairs`データベースの場合の典型的なジョインは 以下になります。

       SELECT name, credit_limit, amount FROM personnel, loans
          WHERE number = 10
          AND personnel.name = loans.name;

上記の場合、 `number = 10`という条件で `personnel`テーブルから検索された レコードセットに限定した上で、 `personnel.name = loans.name` という条件で2つのテーブルのレコードをリンクします。

ジョイン操作でのEmpressのアプローチ手法を以下に示すと

1. 最初のテーブルから限定条件を満たす、すべてのレコードを見つけます。
2. それらの各レコードに対し、 2番目のテーブルから 限定条件を満たし、 最初のレコードと2番目のテーブルのレコードをリンクする句を満足する すべてのレコードを見つけます。
3. また、さらにこのように検索された各レコードに対し、 3番目、4番目... といったテーブルのレコードを見つけます。

最初のテーブルの各レコードに対し、 2 番目のテーブルのレコードの検索は、 最初のアトリビュートについての各句が、 実際において定数比較としておこなわれることに 留意してください。 同様に3番目のテーブルからのレコード検索も、最初と2番目のテーブルの それぞれの属性についての各句が、定数比較として処理されます。

例において、`personnel`テーブルの`number`を基に `personnel`から選択をおこない、 `loans.name`が、`personnel`から取得された `personnel.name`の値と 一致する条件に基づいて、`loans`テーブルの 検索が行われるということになります。 つまり、あらかじめ最初の検索において1つの値（従業員番号10番のKilroyのレコード) に限定されるので、ジョインの際に対象となる loans テーブル からの検索はそのレコード番号による非常に限定されたものになります。

また、前の例において、コマンドの1行目で2つのテーブルを指定している箇所を `personnel`テーブルの前に`loans`テーブルを置いた場合、 最初の選択での条件は全く指定されていないことになります。 このことは、`loans`テーブルの各レコードが、 `personnel`テーブルのすべてのレコードと 比較テストされ、両方のテーブル上にあるレコードを見つけます。 そして、このレコードセットに対し、一致する個人番号のレコードを 見つけるためのチェックをおこないます。 これにより、`loans`テーブルより前に `personnel` テーブルを指定した場合に限り、 `loans`テーブルのレコードは、 `personnel`テーブルの限定したレコードと比較検証されます。

ジョイン処理では、可能な限り素早く大量なレコードを抽出できるよう、 あらかじめテーブルの順序付けすることは非常に効果的です。 上記の例においては、`personnel`テーブルを最初に指定することによって、 対象レコード意外のすべてのレコードが除外されます。

## 3.3 検索とインデックス

リレーションデータベースシステムにおいて、アトリビュート上のインデックスは データ検索の速度向上の機構で、特定のレコードを探す場合のデータの物理位置への 論理ポインタとして利用されます。 ちょうど本の中の索引のように、インデックスは見たい情報を 速く見つけるための支援をします。 しかしながら、Empressが、インデックスを使用するには それを作成する必要があります。 一度、インデックが作成されたならば、 送信した問い合わせごとにインデックスを使用すべきかどうかを Empressが決定します。 例えば、`personnel`テーブルの`name`アトリビュート上に インデックスを作成する場合、以下のように指定します。

       CREATE INDEX personnel_name ON personnel(name);

上記の`personnel_name`は、インデックス名です。 Empress、 `SELECT`コマンドは、検索されるレコードのテーブル アトリビュートのインデックスを必ずしも使用するとは限りません。 まず最初に、インデックスが使用されるためにはインデックスが付けられた アトリビュートを`WHERE`句で参照されなければなりません。 次に、そのアトリビュートが`WHERE`句に適切に位置付けられている 必要があります。例えば以下のような場合、 `personnel.name`のインデックスは使用されません。

       SELECT * FROM personnel WHERE credit_limit > 500;

Empressシステム変数`MSQUERYPLAN`がセットされた場合、 インデックスの使用およびどのようにジョインが実行されたのかに関しての クエリの解析情報を提供します。

`personnel.name`のインデックスを削除するためには、 以下のように入力します。

       DROP INDEX personnel_name;

## 3.4 ビューの作成と使用

Empressでは、1つ以上の物理テーブルに基づく仮想テーブルを 作成することができます。 この仮想テーブルをビューと呼びます。 ビューは、移動可能な窓のように、それを通して データを見ることができます。また、ビューは、 仮想テーブルであるため、 データベースの実際のテーブルとして存在せず、 データ辞書内の SELECT 文の定義として存在します。 例えば、 クレジット制限の個人情報(`personnel`テーブル内)を含めた `loans`テーブルから情報の問い合わせをおこなうためには 以下のように入力します。

       SELECT name, date, amount, credit_limit
          FROM personnel, loan
          WHERE personnel.name = loans.name;

このクエリでは、多くの入力をしなくてはなりませんが、 `loansinfo`というビューを作成し、

       CREATE VIEW loansinfo
          AS SELECT name, date, amount, credit_limit
          FROM personnel, loans
          WHERE personnel.name = loans.name;

それを実行すると

       SELECT * FROM loansinfo;

同じ結果を得ることができます。 Empressは、`loansinfo`という仮想テーブルを作成し、 その検索結果は、`loansinfo`を問い合わせることにより `personnel`と`loans`の情報に基づき検索されます。 ビューの定義を削除するためには、以下のように入力します。

       DROP VIEW loansinfo;

この例での、ビューの基となる 実際のテーブル`personnel`と`loans`は、 ベーステーブルと呼び、また、1つ以上のテーブルを持つ ビューを複合ビューと呼びます。 Empressは、 実際のテーブルのように ビューの操作を許可します。 ただし、複合ビューのある更新操作においては、 基本テーブルの整合性の違反が発生します。 次のセクションでは、 Empressが、更新系の操作(削除、更新および挿入）を許可する条件について 記述します。

## 3.5 ビューの更新系操作

ここでは、 Empressが複合ビューに対しての更新系操作を許可する条件について説明します。 ビューとは仮想テーブルのことで、データはビュー自体には格納されませんが、 テーブルとしての見える構造になっています。 ビューは1つ以上のテーブルに適用したSELECTコマンドを通して定義されます。 ビューからの検索は、 ビューを定義するSELECT コマンドと 同じデータがビューの基となるテーブルから検索されます。 これらの基となるテーブルはそれ自体がビューである可能性があり、 つまり他のテーブルのビューである可能性もあります。 ビューに基となる実際のテーブルをベーステーブルと呼び、 1つ以上の基本テーブルを持つビューを複合ビューと呼びます。 なお、ここで言う更新系操作とは、削除、更新および挿入操作を意味しています。

ビュー上での更新系操作を許可する条件は、通常3つに分類することができます。

1. 削除(delete)操作を許可する基本条件。 この条件には3つの規則があり、これによってビューが基本条件を満たしているかどうかを 判断します。

2. 更新(update)操作を許可する第1追加条件。 基本条件を満たしたビュー(削除を許可するビュー)と、この追加条件が揃えば レコードを更新することが可能です。

3. 挿入 (insert)操作を許可する第2追加条件。 削除と更新を許可する条件を満たしたビューと、この追加条件が揃えば レコードの挿入することが可能です。

### 3.5.1 基本条件

通常の場合、任意の更新系捜査を可能にするためには、ビューはその ベーステーブルに関連するある一定の条件を満たす必要があります。 このことを理解するために、 アトリビュート`a`と`b` を持つテーブル`T1`と アトリビュート`c`と`d` を持つテーブル`T2`の テーブルのジョインから作成された ビュー`V1`を検討してみます。

       T1(a,b)     T2(c,d)     V1(a,b,c,d) = SELECT FROM T1,T
                                                WHERE T1.a = T2.c
       0 0         0 2         0 0 0 2
       1 1         0 3         0 0 0 3

レコード`V1(0 0 0 2)`が、`V1`ビューから削除された場合、 `T1(0,0)`と`T2(0,2)`の各レコードがベーステーブルから 削除され、結果は以下のようになります。

       T1(a,b)     T2(c,d)     V1(a,b,c,d) = SELECT FROM T1,T
                                                WHERE T1.a = T2.c

       1 1         0 3

`V1`から 1 レコードを削除することは、 ビューの 2 つのレコードを削除する結果となります。

この出力結果は、ビューの2件のレコードには、 いずれも`T1(0,0)`のレコードが含まれ、 これに依存していることに留意してください。 一般的には、ベーステーブル中において 1 レコードのデータで構成されている 複数のビューの場合、これらのうちの任意のレコードを 削除するとそのビューからすべてのレコードが削除されます。 こうした事象はビューに限ったことであり、実テーブルでは起こり得ません。 また、このようなテーブルとビューの違いを持たせないために、 Empressでは、基本条件を満たさないビューに 対する更新系操作を認めていません。

基本条件 | ビュー上での更新系操作を許可するためには、 原則的にベーステーブルの任意のレコードが、 ビュー上では多くとも 1 度しか出現することが できないという条件をビューが満たさなければなりません。

ビューが基本条件を満たすかどうかを判断するための3つの規則は以下のとおりです。 これらの規則は、ジョインを含むアトリビュートにおいて、 ビューを定義する`WHERE`句で表されたビューの構造を決定した ジョインの条件と関係があります。

1. 規則 1

   ビューを定義する`WHERE`句は、必ず一連の限定条件を `AND`で結合し、限定しなければなりません。 この一連の条件とは以下に限定されます。

   - アトリビュートと定数の比較
   - 同じテーブル上の2つのアトリビュートの比較
   - 異なるテーブル上の2つのアトリビュート間の等価条件

   これにより、ビュー上での更新系操作を行う場合には ビューを定義する`WHERE`句に以下の条件を含むことはできません。

   - `NOT`条件
   - `OR`結合
   - テーブル間での"="以外の任意の比較

   この規則は、次の規則をより確実なものとします。

2. 規則 2

   任意のベーステーブルのレコードがビューにおいて重複して出現しないようにするためには、 そのビューの`WHERE`句において十分な限定条件が設定されている必要があります。 つまり、ベーステーブルのアトリビュート間での等価条件が満たされるように設定されて いなければなりません。

   その規則が条件を満たしているかどうかを決定するテクニックとして まず各ベーステーブルを点として表現し、さらに2つの各ベーステーブルを 結合している等価条件を個々の点の線として表現します。 ある任意の点から他の任意の点へ複数の線を渡って移動する （必要であるならば中間の点を横切る）ことができる場合は、 その規則は条件を満たしていると言えます。

   この規則の具体例を示すために `T1(a,b,c)`、 `T2(d,e)`、`T3(f,g,h)`、`T4(i,j)` について検討してみます。 以下のジョイン規則は、 点と線の表現を用いれば、すべての線をわたってすべての点の間を移動できる ことから条件を満たしています。 なお、各テーブルのどのアトリビュートが、各条件に関係があるのかということは 重要でないことに留意してください。

          SELECT .... FROM T1, T2, T3, T4
             WHERE T1.a = T2.d AND T2.e = T3.h AND T3.g = T4.i

          SELECT .... FROM T1, T2, T3, T4
             WHERE T1.a = T2.d AND T2.e = T3.h AND T2.d = T4.i

          SELECT .... FROM T1, T2, T3, T4
             WHERE T1.a = T2.d AND T1.b = T3.h AND T1.c = T4.i

   以下のジョインは、点と線の表現を用いれば、すべての線を渡って すべての点の間を移動できないため、この規則を満たしていません。

          SELECT .... FROM T1, T2, T3, T4
             WHERE T1.a = T2.d

          SELECT .... FROM T1, T2, T3, T4
             WHERE T1.a = T2.d AND T2.e = T4.i

          SELECT .... FROM T1, T2, T3, T4
             WHERE T1.a = T2.d AND T3.h = T4.i

   この規則の付則として、同じビュー上においては1つのテーブルを2度、選択すること できません。つまり、あるテーブルのビューを形成するためそのテーブル自体を ジョインした場合にビューに対する更新系操作は全く許可されません。

3. 規則 3

   ベーステーブルの組み合わせのすべての順列において、 2 番目のテーブルのアトリビュートと等価比較する 最初のテーブルのアトリビュートのセットは、 必ずキー(ユニークキー)を含んでいる必要があります。 つまり、1つのアトリビュートのユニークインデックスか、 あるいは複数のアトリビュートのユニーク複合インデックスの 必ずどちらかでなければなりません。 (複合インデックスの場合は、 ジョインの`WHERE`句の一部としてアトリビュート を指定することはできません。) この規則は、 あるテーブル上の 2 つのレコードが別のテーブル上の同じレコードと 結合されることを防ぐために必要です。 最初に記述した例を参照すると

          T1(a,b)     T2(c,d)     V1(a,b,c,d) = SELECT FROM T1,T2
                                                   WHERE T1.a = T2.c
          0 0         0 2         0 0 0 2
          1 1         0 3         0 0 0 3

   この規則では、ユニークインデックスが`T1.a`および`T2.c`上に 存在することを要求します。 従って、テーブル`T2`は、この規則を満たすことはできません。 なお、`T2`テーブルが`(0 3)`レコードを持っていない場合には、 この規則の条件を満たし、結果としてビューは、`T1(0,0)`に対する重複 データを含まないことになります。

   この規則が条件を満たしているかどうかを決定するためのテクニックとして、 ジョインされる各テーブル間の組み合わせを行います。それから、 各組み合わせごとに2番目のテーブルと等価比較に使用される1番目のテーブルの アトリビュートを指定します。 この場合、各リストのアトリビュートのうち最低1つにユニークインデックスか あるいは複数のアトリビュートに対してユニーク複合インデックスが必要です。

   例えば、 `T1(a,b,c)`、`T2(d,e)`、 `T3(f,g,h)`の各テーブルについて 以下の3つの条件を検討してみます。

   - 次のジョイン条件を指定します。

            SELECT .... FROM T1, T2, T3
               WHERE T1.a = T2.d and T2.d = T3.f

     上記を指定した場合、次のリストを作成することができます。

            T1 to T2: T1.a
            T2 to T1: T2.d
            T2 to T3: T2.d
            T3 to T2: T3.f

     この場合、ユニークインデックスは `T1.a`、`T2.d`および`T3.f` に存在する必要があります。

   - 次のジョイン条件を指定します。

            SELECT .... FROM T1, T2, T3
               WHERE T1.a = T2.d AND T2.e = T3.f

     上記を指定した場合、次のリストを作成することができます。

            T1 to T2: T1.a
            T2 to T1: T2.d
            T2 to T3: T2.e
            T3 to T2: T3.f

     この場合、ユニークインデックスは `T1.a`、`T2.d`、 `T2.e`、および`T3.f` に存在する必要があります。

   - 次のジョイン条件を指定します。

            SELECT .... FROM T1, T2, T3
               WHERE T1.a = T3.f AND T2.e = T3.g
               AND T2.d = T3.f

     上記を指定した場合、次のリストを作成することができます。

            T1 to T3: T1.a
            T2 to T3: T2.e, T2.d
            T3 to T1: T3.f
            T3 to T2: T3.g, T3.f

     この場合、ユニークインデックスは `T1.a`、`T3.f` に存在する必要があります。 また、ユニークインデックスが、 `T2.d`または`T2.e`のどちらかに存在するか、 あるいは`T2.d`および`T2.e`上にユニーク複合インデックスが 存在しなくてはなりません。

   Empressでは、3つの基本規則の表すように基本条件を満たすビューからの レコード削除は許可されます。また、更新および挿入操作を許可するためには、 必ず付加条件を満たさなければなりません。

### 3.5.2 第 1 付加条件

再びテーブル`T1(a,b)`と`T2(c,d)`について検討してみます。 このときのビュー`V1`を次のように定義します。

       CREATE VIEW V1 AS SELECT FROM T1, T2
          WHERE T1.a = T2.c;

また、`T1.a`および`T2.c`に対してユニークインデックスがあり、 上記の規則 3 を満たす場合を仮定します。 このときのビュー`V1`は、前説の基本条件をすべて満たします。

       T1(a,b)     T2(c,d)     V1(a,b,c,d) = SELECT FROM T1,T2
                                                WHERE T1.a = T2.c
       0 0         0 2         0 0 0 2
       1 1

`V1`のレコードを `(0, 0, 3, 2)`に更新すると仮定すると、 この更新が許可される場合は、 ビューの定義に違反している場合と同じように そのレコードはビュー上に含まれなくなります。 実際、この更新操作はレコードの削除のように見えます。 これは、`WHERE` 句において等価された 2 つのアトリビュート のビューを含んでいる影響です。

ビューとテーブルの違いを区別する特徴を持たせないようにするためには、 Empressではこのようなビューにおいての更新操作を許可していません。

ビューのためにテーブルをジョインする場合、 ベーステーブルのアトリビュートの等しい組み合わせは、 等しくなるように指定された２つ以上のテーブルの アトリビュートセットの結果になります。

第 1 付加条件は以下になります

第 1 付加条件 | ベーステーブル上のすべてのアトリビュートセットが等しくなければならなく、 それぞれのセットからは1つのアトリビュートだけがビュー上に現れなければなりません。

ビューを更新する場合、このような1つのセット内のすべてのアトリビュートに対し、 出現したアトリビュートに指定された値が割り付けられます。

この条件に違反するビューは。さらに問題発生の原因となります。

テーブル`T1(a,b)`および`T2(c,d)`について再度検討してみます。 このとき`T1.a`および`T2.c`にユニークインデックスが存在し、 ビュー`V1`を以下のように定義します。

       CREATE VIEW V1 AS SELECT b, c, d FROM T1, T2
          WHERE T1.a = T1.b AND T1.a = T2.c;

以下のデータの場合

       T1(a,b)     T2(c,d)     V1(b,c,d) = SELECT b, c, d FROM T1,T2
                                             WHERE T1.a = T1.b AND T1.a = T2.c
       0 0         0 2         0 0 2
       1 1

`V1`のレコードを`(0, 1, 2)`に更新しようとした場合、 不明瞭な結果がもたらされます。 ビューを定義する`WHERE`句は、 `T1.a`アトリビュートを`T1.b` と`T2.c`の両方に一致させます。 こらら 2 つのアトリビュートが異なる値を割り付けられた場合、 どのような値が`T1.a`に割り付けられるかは不明瞭です。 従って、一致するように指定したアトリビュートセットの中の1つだけを含むように 制限することで、こうした不明瞭な結果が発生しないようにすることができます。

Empressでは、基本条件と同様にこの第1付加条件を満たす ビューに対し、レコードの更新を許可します。

ビューを定義する`WHERE`句でアトリビュートを一致するように 指定するためにはそれらのアトリビュートは必ず同じデータ型でなければなりませんが、 アトリビュートは、異なるデータ型のパラメータで定義されることもあるので、 アトリビュートを別のサイズまたは別の文字データのタイプに格納することが可能です。 ビューに対して入力されるデータは、任意のアトリビュートに 合うようには切り詰められません。 任意のアトリビュートに挿入できない場合には、 その操作はそれ以上処理されずエラーメッセージが表示されます。

ベーステーブル上の任意のアトリビュートが原因で変換エラーが発生した場合、 その操作はそれ以上処理されずエラーメッセージが表示されます。

### 3.5.3 第 2 付加条件

第 2 付加条件は、"not null" を定義するアトリビュート、 またはユニークインデックスを持つアトリビュートに関する条件です。 このようなアトリビュートは、NULL 値を挿入することができないため、 ビューにおいて挿入操作が許可されている場合には、 必ずそれらのアトリビュートがビュー上に表示されるか あるいはビュー上に実際に表示されるアトリビュートと一致する 必要があります。 このことは、ビューにレコードが挿入される場合に、 ビュー上に表示されないベーステーブルの任意のアトリビュートに対して その値としてNULL値が挿入されるためです。

第 2 付加条件は以下のように定義されます。

第 2 付加条件 | 任意のベーステーブル中の`NOT NULL`アトリビュートは、ビュー上にあるか あるいはビュー上のアトリビュートと一致しなくてはなりません。

Empressは、基本条件と同様に第1付加条件および第2付加条件を満たす ビューにレコードを挿入することを許可します。

ビューに対し更新または挿入されるデータが、 ベーステーブル上でのユニークインデックス制約と矛盾する場合や ベーステーブル上の既存のレコードがそのテーブル上に挿入される レコードと一致する場合には、 その操作の条件を満たすために既存のレコードが採用されます。

ビュー `V1`を以下のような定義をした場合について検討してみます。

       CREATE VIEW V1 AS SELECT a, b, d FROM T1, T2
          WHERE T1.a = T2.c;

以下のデータを使用します。

       T1(a,b)     T2(c,d)     V1(a,b,d) = SELECT a, b, d FROM T1, T2
                                              WHERE T1.a = T2.c
       0 0         0 0         0 0 0
                   1 1

`T1.a` および `T2.c`上にユニークインデックスが存在するため、 レコードをそのビューに挿入することができます。 レコード`(1, 1, 1)`をビューに挿入した場合、 その実行結果は以下のとおりです。

       T1(a,b)   T2(c,d)   V1(a,b,d) = SELECT a, b, d FROM T1, T2
                                          WHERE T1.a = T2.c
       0 0       0 0       0 0 0
       1 1       1 1       1 1 1

`T2`に挿入するためのレコード`(1, 1)`は、 既に`T2`の 2 番目のレコードと一致しています。 `T2.c`上にユニークインデックスがあるため、 2 番目のレコード`(1, 1)`はそのテーブルに挿入することはできません。 従って、`T2`の挿入操作を満たすために 既存のレコード`(1, 1)`が採用され、 `T1`への挿入操作は正常に処理されます。

## 3.6 分散データのビュー

ビューを定義することにより、 異なったファイルシステムまたはネットワーク上にある複数のデータベースの データにアクセスすることができます。 ビューが更新系操作できるように適切に定義されている場合、 デフォルトのデータベースのテーブルと機能的に区別がつきません。 このセクションでは、分散データへのアクセス方法を例によって示し、 ビューのサンプルを紹介します。

最初に2つのデータベースを作成します。

       empmkdb /usr/db1
       empmkdb /usr2/db2

上記のいずれかのデータベースに対し`empsql`を起動します。

       empsql /usr/db1

各データベース上にテーブルを作成します。

       CREATE tab1 (a1, b1);
       CREATE "/usr2/db2":tab2 (a2, b2);

これにより、 データベース`db1`上にビュー`db2:tab2`を作成することができます。 `db1`:

       CREATE VIEW tab2 AS SELECT FROM
       "/usr2/db2":tab2;

ビュー`db1:tab2`は、 検索、挿入、更新、削除に関する限りでは テーブル`db2:tab2`と機能的に区別はつきません。 （これらの操作に関して同じユーザーアクセス権限を保有している場合）

ここで、`tab1`と`tab2`の両方をアクセスすることができる ビュー`tab12`を作成します。 `tab2`へのアクセスは、テーブル`db2:tab2`または ビュー`db1:tab2`のどちらかを通して行うことができます。 例では、ビューdb1:tab2を使用します。

       CREATE VIEW tab12 AS SELECT tab1.a1, tab1.b1,tab2.b2
          FROM tab1, tab2 WHERE tab1.a1 = tab2.a2;

このビューは、 `tab1`と`tab2` のジョインにより ビュー上での更新系操作の許可するために必要なジョイン条件を満しますが、 挿入操作を許可するためには、ジョイン条件のアトリビュートは 必ずユニークインデックスを持つ必要があります。

       CREATE UNIQUE INDEX ON tab1.a1;
       CREATE UNIQUE INDEX ON "/usr2/db2":tab2.a2;

インデックスは、ビュー上にではなくテーブル`tab2`上に 置かれなければなりません。（インデックスをビュー上に置くことはできません。）

ビュー`tab12`は通常のテーブルと同じように操作できます。

       INSERT tab12 VALUES (1,2,3,4,5,6,7,8,9);
       SELECT FROM tab12;

       a1    b1     b2

       1     2      3
       4     5      6
       7     8      9

       SELECT FROM tab1;

       a1    b1

       1     2
       4     5
       7     8

       SELECT FROM tab2;

       a2    b2

       1     3
       4     6
       7     9

       DELETE FROM tab12 WHERE a1 = 1;
       SELECT FROM tab12;

       a1    b1     b2

       4     5     6
       7     8     9

## 3.7 2次問い合わせ

2次問い合わせ（サブクエリ）は、 `WHERE`句内でのネストされた別の `SELECT`文になります。 `WHERE`句においての2次問い合わせは、比較演算子の右側に 指定します。2次問い合わせの構文は以下のとおりです。

|  |  | `[` | `|ANY ||ALL` | `|] (select_command) |`

`WHERE`句の単純な形式は以下のとおりです。

|  |  | `WHERE attr [NOT]` | `| = |!= | < |<= | > |>= |IN` | `| [ | | | | | |` | `|ANY |ALL` | `|] (select_command) |`

`ANY` を使用した場合、 ネストされた検索によって返された値の 少なくとも 1 つの値に対し、その比較演算子が真であれば、 ブール式は真となります。 また、ネストされた検索によって返されたすべての値に対し、 その比較演算子が偽である場合やネストされた検索が全く値を返さない場合には ブール式は偽となります。

`ALL` を使用した場合、 ネストされた検索によって返された値の うちすべての値に対し、 その比較演算子が真である場合やネストされた検索が全く値を返さない場合には ブール式は真となります。 また、ネストされた検索によって返された値の 少なくとも1 つの値に対し、そのその比較演算子が偽であれば、 その結果は偽となります。

2 次問い合わせを利用して、比較対照する値のリストを生成することが可能です。 例えば、次のとおりです。

       SELECT ... WHERE attr [NOT] IN (SELECT attr...);

加えて、`WHERE`句のブール式は、以下のように指定することができます。

       EXISTS subquery_command

2次問い合わせ`EXISTS`を使用した場合、 `SELECT`コマンドにおいて 1 件以上 レコードが検索された場合、 `WHERE`句は真となります。

2次問い合わせは、`SELECT`文と同様に `UPDATE` や `DELETE`文の`WHERE`句で 使用することができます。

### 3.7.1 2次問い合わせの例

以下の`WHERE`句では、ローン全体の平均値を下回るすべてのローンを検索します。 また、その指定において平均値を求めるためのネストした`SELECT`文を使用します。

       SELECT FROM loans
          WHERE amount < (SELECT AVG (amount) FROM loans);

出力結果は以下のようになります。

       name                                   date        amount

       Jones                      7 February  1992        $33.95
       Wladislaw                 27 February  1992        $55.00
       Jones                      3 April     1992        $25.00
       Wladislaw                 12 May       1992        $25.00
       Peterson                   6 June      1992        $50.00
       Wladislaw                 25 June      1992        $75.00

次のコマンドは、具体的に`ANY`を使用して、 `personnel`テーブル中に登録されている人の ローンを検索します。

       SELECT FROM loans WHERE name = ANY (SELECT name FROM personnel);

出力結果は以下のようになります。

       name                                   date        amount

       Mosca                      2 February  1992       $150.00
       Jones                      7 February  1992        $33.95
       Kilroy                    16 February  1992       $250.00
       Wladislaw                 27 February  1992        $55.00
       Jones                      3 April     1992        $25.00
       Mosca                      4 May       1992       $200.00
       Wladislaw                 12 May       1992        $25.00
       Peterson                   6 June      1992        $50.00
       Wladislaw                 25 June      1992        $75.00
       Jones                     12 August    1992       $300.00
       Scarlatti                  9 September 1992       $150.00
       Scarlatti                 14 October   1992       $125.00

同等のコマンドとして`IN`を使用した例を示します。

       SELECT FROM loans WHERE name IN (SELECT name FROM personnel);

出力結果は以下のようになります。

       name                                   date        amount

       Mosca                      2 February  1992       $150.00
       Jones                      7 February  1992        $33.95
       Kilroy                    16 February  1992       $250.00
       Wladislaw                 27 February  1992        $55.00
       Jones                      3 April     1992        $25.00
       Mosca                      4 May       1992       $200.00
       Wladislaw                 12 May       1992        $25.00
       Peterson                   6 June      1992        $50.00
       Wladislaw                 25 June      1992        $75.00
       Jones                     12 August    1992       $300.00
       Scarlatti                  9 September 1992       $150.00
       Scarlatti                 14 October   1992       $125.00

すべての人のクレジットの限度額を越えたローンを検索するためには 次のように指定します。

       SELECT FROM loans WHERE amount >
          ALL (SELECT credit_limit FROM personnel);

出力結果は以下のようになります。

       name          date               amount

限度額を越えたローンは見つかりませんでした。

## 3.8 日付計算

`DATE` および `TIME` アトリビュートは、 そのアトリビュート自体で算術演算することができ、 年、月、日などをアトリビュートに基づいたデータを抽出することが 可能です。 以下のキーワードは、日付、または時間に関係のある式において 使用することができ、また、そのキーワード自体を式の中で簡単に使用すること できます。この場合、これらの結果は整数に変換されます。

       year       years      yearof
       month      months     monthof
       week       weeks      dayof
       day        days       hourof
       hour       hours      minuteof
       minute     minutes    secondof
       second     seconds    weekof

`yearof`、`monthof`、`dayof`演算子は、 整数値を生成します。(年は 0 またはそれ以上の値。月は 1 〜 12、 週は 1 〜 52、日は 1 からその月までの日数) `hourof`、 `minuteof`、`secondof`演算子も同様に 整数値を生成します。(時間は 0 〜 23、分および秒は 0 〜 59)

`TIME`アトリビュートではなく `DATE`アトリビュートに対し、 `hourof`、`minuteof`、`secondof` を行った場合、結果として 0　が返されることに留意してください。

また、式に対して日付を指定するときには、 正確に認識できるように 必ず国際標準日付フォーマット(yyyymmdd)に従うか、 あるいは日付ピクチャ変数のMSDATEPICに指定した順序で 日、月、年を対応する必要があることに注意してください。 (`MSDATEPIC`変数に関しての 詳細な情報はEmpress: データベース管理者ガイド) を参照してください。)

例として、以下に日付の計算を示します。

       SELECT date + 2 months CONVERT TO DATE (1) FROM loans;

この出力結果は以下になります。

       convert (date + (2 months)) to date

                          2 April     1992
                          7 April     1992
                         16 April     1992
                         27 April     1992
                          3 June      1992
                          4 July      1992
                         12 July      1992
                          6 August    1992
                         25 August    1992
                         12 October   1992
                          9 November  1992
                         14 December  1992

`dayof`演算子を使用した例を以下に示します。

       SELECT dayof date FROM loans;

この出力結果は以下になります。

       (dayof date)

                  2
                  7
                 16
                 27
                  3
                  4
                 12
                  6
                 25
                 12
                  9
                 14

以下の例は、 2 つの日付を加算しても意味がありませんが、それらを減算に使用することで意味があり、 その実行結果は、両方の日付の間の差となります。 それぞれのローンの期間を見つけるために 現在の日付が 1992 年 1 月 1日として、`MSDATEPIC`のデフォルト値が "dd aaaaaaaaa yyyy"であると仮定する場合に 次のコマンドを指定します。

       SELECT name, "31 Dec 1992" - date FROM loans;

この出力結果は以下になります。

       name                 ("31 Dec 1992" - date)

       Mosca                                   333
       Jones                                   328
       Kilroy                                  319
       Wladislaw                               308
       Jones                                   272
       Mosca                                   241
       Wladislaw                               233
       Peterson                                208
       Wladislaw                               189
       Jones                                   141
       Scarlatti                               113
       Scarlatti                                78

整数値を評価する式である場合は、 `years`、 `months`、 `days` などが式の前にあっても構いません。

以下は、式のためのヘッダーを生成する例になります。

       SELECT name, '31 Dec 1992' - date PRINT "outstanding" FROM loans;

この出力結果は以下になります。

       name                       outstanding

       Mosca                              333
       Jones                              328
       Kilroy                             319
       Wladislaw                          308
       Jones                              272
       Mosca                              241
       Wladislaw                          233
       Peterson                           208
       Wladislaw                          189
       Jones                              141
       Scarlatti                          113
       Scarlatti                           78

## 3.9 式の値からデータタイプへの変換

`SELECT`コマンドにおいて 式の結果の値を、最適な汎用的なデータタイプ に暗黙的に変換します。 汎用的なデータタイプは、 明示的なパラメータ定義（サイズなど）のないデータタイプです。 このタイプは、明示的に特定のデータタイプに変換することができます。 実際、この変換は 正しくフォーマットされた出力を生成する場合において 最も便利な方法であると言えます。

式を他のデータタイプに明示的に変換するための一般的な指定フォームは 次のとおりです。

       CONVERT expr [TO] data_type

       expr CONVERT TO data_type

標準データタイプのうちの1つか、 (例えば`CHAR` (40,1)、`DATE`(3)、 `SHORTINTEGER`、 `DOLLAR` (4,2)など) あるいは `GENERIC`キーワードの後に `CHAR`、`INTEGER`、 `DECIMAL`、 `FLOAT`、 `DATE`、`EXTERNAL`のいずれかを指定します。 `GENERIC EXTERNAL`は、値をすべて文字列として出力します。

これらの変換はネストして使用することも可能です。

以下に、いくつかの例を示します。

       SELECT amount, amount * 1.05 CONVERT TO DOLLAR(4,1)
          PRINT newamount FROM loans;

出力結果は以下のとおりです。

       amount        newamount

       $150.00         $157.50
       $33.95           $35.64
       $250.00         $262.50
       $55.00           $57.75
       $25.00           $26.25
       $200.00         $210.00
       $25.00           $26.25
       $50.00           $52.50
       $75.00           $78.75
       $300.00         $315.00
       $150.00         $157.50
       $125.00         $131.25

次のコマンドを入力します。

       SELECT date, date + 6 months
          CONVERT TO DATE(1)
          PRINT Õdue dateÕ WIDTH 20
          FROM loans WHERE name = 'Jones';

出力結果は以下のとおりです。

                    date                 due date

        7 February  1992         7 August    1992
        3 April     1992         3 October   1992
       12 August    1992        12 February  1993

次の例は、1992年8月1日を基準として3ヶ月以上経過している すべてのローンに関して、24％の利息を日割り計算により算出します。 最初にローンの日付から8月1日までの間の日数を計算する必要があります。 このときアプリケーションは、必ずそのローン期間が3ヶ月以上であるかどうかを チェックしなければなりせん。 3ヶ月以上経過している場合は、日数に 0.24/365 を乗算し、 その利息を計算してから、その値をドルに変換します。 日付ピクチャ変数の`MSDATEPIC`を使用して、 "dd aaaaaaaaa yyyy"のデフォルト値に変換するには 以下のコマンドを入力します。

       SELECT date, amount,
          (amount * ("1 August 1992" - date) * 0.24 / 365)
          CONVERT TO DOLLAR(4,1)
          PRINT interest FROM loans
          WHERE "1 August 1992" > (date + 3 months);

この実行結果は以下のとおりです。

                    date        amount        interest

        2 February  1992       $150.00          $17.85
        7 February  1992        $33.95           $3.92
       16 February  1992       $250.00          $27.45
       27 February  1992        $55.00           $5.64
        3 April     1992        $25.00           $1.97

## 3.10 データベース特権

データベースの作成者は、データベース管理者(`DBA`)であり、 データベース管理者は、データベース上のテーブルに任意の特権を 与える権限を持っています。 データベースが作成されたときにEmpressは、 システムテーブルを含むデータ辞書を同時に作成します。 デフォルトでは、データベース管理者は、 権限の付与とともにデータ辞書内のシステムテーブルに対し、 `USER`特権(delete, display, insert,select, update)が 与えられます。 つまり、データベース管理者は、データ辞書内のテーブルにこれらの特権を 実行することが可能で、またこれらの特権を他に与えることができます。 データベース管理者は、いつでも誰にでもシステムテーブルに対する `ALL`特権を与えることができることに注意してください。 また、データベース管理者は、データ辞書テーブルに `ALTER`、 `DROP`、 `EMPTY`特権の使用を 絶対にしないでください。

新しいテーブルを作成するためには、 ユーザーは、データ辞書の`sys_tables`テーブルに insert 特権を持っている必要があります。 デフォルトによって、初期値として データベース管理者だけがデータベースに テーブルを作成することができる特権を持っています。 (これは`USER`特権の 1 つです。) 他のユーザーがテーブルを作成できるようにするためには データベース管理者は、必ず他のユーザーに対して、`DBA`(付与者)として `sys_tables`に insert 特権を与えなければなりません。 ユーザーではなくデータベース管理者として特権を与えておくことで データベース管理者が代わった場合でも新しいデータベース管理者は、 以前に与えられた特権を簡単に識別し、削除することができます。

テーブルの作成者は、テーブルの`CREATOR`となります。 `CREATOR`は、そのテーブルに対する任意の特権を与える権限が付与されます。 各テーブルの`CREATOR`はデータ辞書内の `sys_tables`テーブルにおいて名前付けされます。 データベース管理者または十分な特権を付与されているユーザーは 任意のテーブルの`CREATOR`を変更することができます。

テーブルの作成時に`CREATOR`に対して、そのテーブルの特権が与えられます。 与えられる特権は`tabzero`ファイル中の管理変数`MSDBPRIVS`によって 指定されたデフォルトの値です。 このデフォルトの指定は、`CREATOR`には `DBA`タイプの特権を与え、 テーブルを作成したユーザー名（`USER`はシステムのユーザーID）には `USER`タイプの特権を与えます。 デフォルトによって特権が与えられない場合、 データベースの`DBA`およびテーブルの`CREATOR`は、 その特権自体を与えるかあるいはテーブル上の任意の特権を他のユーザーに 与えることができます。

テーブルの作成したユーザーが、 他のユーザーからでもそのテーブルにアクセスできるようにしたい場合、 そのユーザーは、アクセス権限を与えるか、 または適切な特権(`PUBLIC`として) を与える必要があります。 `GRANT OPTION`付きで特権が与えられた場合、 被付与者によって特権を他のユーザーに与えることが可能です。 ただし、`DBA` と`CREATOR`には、 テーブルの特権全般にわたり管理を維持できます。 これは特権を付与した付与者として テーブル上の任意の特権を削除することが可能な方法です。 データベース管理者のこのような面で、 すべてのデータベース上のテーブルに幅広く管理します。

Empress の使用するファイルのアクセス権限は、 各データベースのオペレーティングシステムに基づいて設定されます。 Empressデータベースのテーブルをアクセスをするためには、 ユーザーは適切なオペレーティングシステムファイルのアクセス権限と 適切な特権の両方を持つ必要があります。

## 3.11 トランザクション

トランザクションは、問い合わせ言語コマンドセットの中の1つで、 処理を1つの単位として扱います。 トランザクション処理中においては、 そのトランザクション中にアクセスされたデータベースに関する すべての操作が、トランザクションの一部として扱われます。 トランザクションは以下の2つの理由により有用です。

1. トランザクション処理中において、 コマンドによるさまざまな変更 （テーブルの変更、レコードの挿入、削除、更新など） を行っても、永続化するコマンドを実行しない限り反映されないため、 データベース上で取り返しのつかない影響を与えずに済みます。 必要であれば 変更を永続化する変わりにすべてをロールバックすることで データベースをトランザクションの開始前の状態に戻すことができます。

2. ディスク障害およびネットワーク異常が発生した場合の トランザクションの整合性は、 Empressのウォームリスタート機能によって保証されます。 また、複数のデータベースにわたるトランザクションの整合性は、 2 フェーズコミットプロトコルによって保証されます。 システム障害などによって中断されたトランザクションは、 1つの単位として解決されます。

ワークセッションを複数のトランザクションに分割しておくことで、 データベース操作の柔軟性とセキュリティを向上することができます。 また、トランザクション中のコマンドで望ましい実行結果を得られない場合、 そのコマンドを取り消して再度実行することが可能です。 これは、例えば不適切なテーブルに対する削除などのユーザーエラーを、 その一連のコマンドを再実行することで減らすことになります。

トランザクション操作は、以下のコマンドを使用します。

- `START WORK`コマンドにより開始します。
- `COMMIT WORK`コマンドにより変更を永続化します。
- `ROLLBACK WORK`コマンドにより取り消します。

また

- `SAVEPOINT`コマンドによりトランザクションのセーブポイントを設定します。
- `DISPLAY WORK`コマンドによりトランザクションの状態を表示します。

表 3-1 トランザクションコマンド

コマンド | 説明
`START WORK;` | トランザクションの開始
`SAVEPOINT savepoint;` | セーブポイントの定義
`ROLLBACK WORK TO savepoint;` | セーブポイントまでのロールバック
`DISPLAY WORK;` | カレントトランザクションのステータス表示
`ROLLBACK WORK;` | トランザクションのロールバックを実行し、終了します。
`COMMIT WORK;` | トランザクションのコミットを実行し、終了します。

上記のトランザクションコマンドの詳細な構文の情報は、 Empress SQL: リファレンス マニュアルを参照してください。

### 3.11.1 ネストしたトランザクションとセーブポイント

トランザクションは、ロールバックオプション機能を備えてるいるため、 セーブポイントを指定することによってネストすることができます。 トランザクションを実行中に`SAVEPOINT`コマンドを使用して トランザクション上でのセーブポイントの設定が可能です。 このセーブポイントによって、ネストしたトランザクションが開始されたことに なり、ネストされたトランザクションは、 セーブポイントの名前を指定して`ROLLBACK WORK`コマンド を実行することができます。 セーブポイントの後のすべてのコマンドはロールバックされ、 ネストされたトランザクションは開始前の状態の 元のトランザクションに戻ります。

実質的にはトランザクションの長さやネストの階層には全く制限はありません。 ネストされたトランザクション内では、あらかじめ任意に名前付けれた セーブポイントにロールバックすることが可能で、またトランザクション全体を ロールバックすることもできます。

トランザクションは段階的にコミットすることはできず、 `COMMIT WORK`コマンドは常にトランザクションのすべてのコマンドを コミットします。

### 3.11.2 自動トランザクション

Empressでは、それぞれの問い合わせ言語コマンドを1つのトランザクションとして 扱うことが可能で、このトランザクションは次のコマンドが正常に入力されたときに コミットされます。 この自動トランザクションと呼ばれる機能を使用することにより、 他のコマンドを実行する前に`ROLLBACK WORK`コマンドを実行し、 最後に入力したコマンドを取り消すことが可能です。 無効なコマンド（例えば構文エラーのあるコマンド）また `CHANGE`、 `EDIT`、`HELP`、 `HOLD`、 `RECALL`、`SET`、 `STORE`、 `UNSET` の実行コマンドは 前のコマンドのコミットおよび新しいトランザクションを 開始はしません。 ただし、`RUN`コマンドの場合は、前に実行したコマンドに よって、トランザクションをコミットする場合としない場合があり、 前に示したコマンドリストでは、トランザクションはコミットされませんが、 それ以外のコマンドである場合にはトランザクションはコミットされ 新しいトランザクションが開始されます。

コマンド単位の自動トランザクションを有効にするためには、 Empressシステム変数の`MSQLTRANSACTION`を設定します。

       SET MSQLTRANSACTION TO X;

自動トランザクションを無効にするには、 `MSQLTRANSACTION`の設定を解除し、 `COMMIT WORK`または`ROLLBACK WORK`コマンドを実行 することにより、最後のコマンドをコミットあるいはキャンセルします。

       UNSET MSQLTRANSACTION;
       COMMIT WORK;

または

       UNSET MSQLTRANSACTION;
       ROLLBACK WORK;

### 3.11.3 トランザクションのロック

トランザクション実行中は、ロックレベル(`LOCK LEVEL`)をテーブルに設定 することを推奨します。 （`LOCK LEVEL`の詳細は `LOCK LEVEL`コマンド Empress: SQL リファレンスを参照してください。) Empressシステム変数`MSTRANSTABLELOCK`が設定されている場合、 トランザクション実行中にそのトランザクションに関係のあるテーブル に対して自動的にロックレベルをテーブルに設定します。 この設定によって、指定したテーブルが正常に読み込まれると それと同じ順序で全く同じレコードが表示されることを保証します。

この変数は次のように設定することができます。

       SET MSTRANSTABLELOCK TO X;

解除するには次のように設定することができます。

       UNSET MSTRANSTABLELOCK;

トランザクション実行中のテーブルロックは、自動トランザクション実行中でも 同じように有効です。 つまり、あるコマンドを入力してから次のコマンドを入力するまでは テーブルがロックされます。

`LOCK TABLE`コマンドは、トランザクション実行中に明示的に テーブルロックをするために使用することも可能です。 ただし、`LOCK TABLE`コマンドを有効にするため、 必ずそのテーブルに対してなんらかのロックレベルを定義する必要があります。

### 3.11.4 トランザクションログのコメント

Empressシステム変数`MSTRANSCOMMENT`は、 トランザクションのコメントをつけるために使用することができます。 `MSTRANSCOMMENT`の値は、`DISPLAY WORK`コマンドによって トランザクションのコメントとして表示されます。

コメントは、複数のユーザーが１つのデータベース上で複数のトランザクションを 実行している場合に有用です。 トランザクション実行中のユーザーを識別するためにコメントを使用する場合は、 `DISPLAY WORK`コマンドでこの情報を利用できます。

### 3.11.5 トランザクションステータスの表示

問い合わせ言語`DISPLAY WORK`コマンドは、 トランザクションのステータスを表示します。 このセクションでは、このコマンドの一般的な使用法について説明します。 このコマンドの完全な構文は Empress SQL: リファレンスに 提供されています。

`DISPLAY WORK`コマンドは以下の３つの形式があります。

1. `DISPLAY WORK;`

   このコマンドはカレントのトランザクションステータスの表示します。 トランザクション中でのみ使用可能です。

2. `DISPLAY WORK ON database [ALL];`

   このコマンドは、進行中のトランザクションを識別し、 データベースのステータスを表示します。 (`ALL`オプションなしで指定した場合は要約した情報をリストし、 `ALL`オプションを指定した場合は完全な情報をリスト表示します。) システム障害後に入力すると、 データベースに影響するすべての未解決なトランザクションの 識別コードのリスト表示します。

3. `DISPLAY WORK id;`

   このコマンドは、トランザクションが持つ識別コード`id`の 完全なステータス情報を出力します。 オプションとして`database:`をトランザクションの識別コードの 前にに指定することも可能です。 `database:`プリフィックスが指定されない場合は、 現在のデフォルトデータベースが前提となります。 このコマンドはどのような場合においても、 データベースのトランザクションのステータスの問い合わせです。 （トランザクションがデータベースに何も影響を与えない場合、 たとえその識別コードが有効であってもトランザクション `id`は認識されません。）

   `DISPLAY WORK` コマンドのこの指定形式は、 トランザクションの全般的なステータス情報を表示をはじめてする コーディネーターおよび各パーティパントに関するステータス情報を 完全で詳細を表示するために利用することができます。

### 3.11.6 　2フェーズコミット

1つのトランザクションは、複数のデータベース上のテーブルに影響を与えることが 可能です。 通常、複数のデータベース上に影響を与えるトランザクションは、 概念および操作の上では単一のトランザクションとして扱われますが、 Empress内部では各データベース上の独立したトランザクションとして 管理および調整しています。 このような状況においてのデータ整合性を保証するために トランザクション処理は 2 フェーズコミットプロトコル を使用し、調整されます。

Empressは、複数のデータベースに渡るトランザクションを認識すると 強制的に 2 フェーズコミットプロトコルをおこない、 影響のあるデータベース全体に対し、一貫した回復の保証をします。 また実行するトランザクションは1単位として制御されます。 `DISPLAY WORK`コマンドとウォームリスタート機能は、 すべてのデータベース上にわたるトランザクションを1単位として扱うため データの整合性が維持されます。 通常の環境下では、トランザクションによって影響を受ける 個々のデータベースを意識する必要はありません。

### 3.11.7 複数データベースにわたるトランザクション

1つのトランザクションが複数のデータベースにわたる場合、 最初にアクセスされたデータベースは、 そのトランザクションに対する コーディネータとして識別されます。 このコーディネータの存在は、該当するトランザクションを コミット、またはロールバック可能であるかを最終的に 判断することにあります。 コーディネータは、 そのデータベースおよび他のデータベースのいずれの場合において システム障害の後にトランザクションに行ったすべての処理を含む トランザクションの進行中のステータス情報を管理します。 また、トランザクションに関係のある他のデータベースを パーティシパントとして知られています。

コーディネータには、トランザクションステータス情報が完全に知らされます。 従って、ウォームリスタートが必要な場合には、 コーディネータが、そのトランザクションを コミットまたはロールバック可能であるかどうか判断することができます。 ほとんどの場合、 パーティシパントにもトランザクションステータス情報を持っているため、 コーディネータとは独立してトランザクションをコミットまたは ロールバックが可能かどうかを判断することができます。 ただし、パーティシパントの場合、適切な処理を決定するために コーディネーターに対してアクセスしなければならないため 多少時間がかかります。

ウォームリスタートユーティリティをデータベース上で実行する場合、 システムエラー発生時に処理中であったデータベースに関係のある トランザクションが複数存在する可能性があります。 この見つかったトランザクションごとに、データベース上のトランザクションを 復元するために行うべき適切な処理を決定しなければなりません。 データベースがコーディネータであるトランザクションの場合、 トランザクションステータスにより判断され、適切な処理がおこなわれます。 データベースがパーティシパントであるトランザクションの場合、 トランザクションステータスの判断の試みは行われますが、 実際の処理はおこなわれません。

### 3.11.8 ウォームリスタート

ウォームリスタート機能はシステムエラーなどが原因でトランザクションが中断 された場合に、データ保護を保証するために提供されています。 実行中のタスクエラー、オペレーティングシステムのエラー、 ディスク障害などのエラーの発生の際に エラー発生時のトランザクション内で何らかのデータベース操作が行われた 場合にウォームリスタート機能によりデータベースをある一定の状態に まで復元が可能です。

データベースの復元を可能にするには、 ウォームリスタート機能ではディスクが正常であるという前提条件が必要です。 物理データがディスク上から消失した場合は、 `emprecov`ユーティリティを使用してアーカイブデータベースから 復元をします。

Empressでトランザクションを実行する場合、 オプションとしてトランザクションにおいて、 すべての操作をデータベースに最終的にコミットおよび、 ロールバックすることができます。 トランザクション処理中に中断されると、そのトランザクションは 未解決な状態となり、データベースは矛盾したデータを 含んでいる可能性があります。 ウォームリスタート機能を使用することで、何らかの影響を受けたデータベース をリスタートし、未解決なトランザクションも解決することができます。 また、データベースがリスタートされる際、 そのデータベースに関係のある中断されたトランザクションは 完了するか、またはロールバックするかのどちらかで 処理することができます。

自動ウォームリスタートユーティリティ `empwarm` は、 Empress: データベース管理者ガイド. で説明されています。

### 3.11.9 マニュアルウォームリスタート

マニュアルオーバーライド処理は、以下のいずれかの状況での使用のために提供されています。

1. 自動ウォームリスタートユーティリティを使用し、 ロールバックされることによって、トランザクション全体を失いたくない場合

   多くの時間と労力を費やした非常に重要なトランザクションであると場合、 そのトランザクション上にセーブポイントを指定したいと考えて当然です。 このトランザクションが中断されても、設定したセーブポイントまでトランザクション を復元できます。 この場合、そのトランザクションステータスを検証することができ、 そのトランザクションがウォームリスタートによってロールバックできるのであれば、 その代わりにセーブポイントまでのトランザクションを復元できます。 セーブポイントへの復帰 については、次の項を参照してください。

2. パーティシパント上のトランザクションを速やかに解決する必要があり、 コーディネータとのコミュニケーションが確立できない場合。

   パーティシパント上のトランザクションを解決する必要があり、 コーディネータとのコミュニケーションが確立できない場合、 パーティシパント上で強制的にトランザクションを解決することができます。 多くの場合、 パーティシパント上で`DISPLAY WORK`コマンドを指定すると 正しい解決方法（コミットまたはロールバック）が提示されますが、 複数のデータベースにわたるトランザクションの場合、 トランザクションをコミットまたはロールバックするかどうか 判断できない状態のままパーティシパントが中断される 可能性があります。 これは、パーティシパントが自ら適切な処置を施す判断ができない場合や 適切な処置の方法を知るためにコーディネータにアクセスすることができない 場合になります。（コーディネータは、必ずトランザクションをコミットまたは ロールバックするかどうか判断できます。） このような場合おいては、 コミュニケーションを再確立するか、 あるいは他のコーディネータのサイトに接続するし、 `DISPLAY WORK`コマンドを使用して 正しい処置を施す判断をするまで 待たなくてはなりません。

   トランザクションを解決するための正しい処置を判断したときには 以降で説明する`COMMIT WORK`(強制的)または `ROLLBACK WORK`(強制的)コマンドのどちらかを使用して、 パーティシパントのトランザクションをコミットまたはロールバックします。

   パーティシパント上で強制的にトランザクションをコミットまたは ロールバックした場合、パーティシパントに関するステータス情報は、 そのパーティシパントのデータベースから削除されます。 通常、パーティシパントはコーディネータにその強制処置について知らせますが、 コーディネータにアクセスできないな場合、そのパーティシパントの 最後のステータスについてはコーディネータに知らせることはできません。 さらに、コミュニーケーションが再確立されている場合には、 コーディネータはその最後のステータスを取得することはできません。 コーディネータはこの状況をディスク障害の可能性があるものとして評価し、 判断するため、自動ウォームリスタートユーティリティによる処置は全く行いません。 従って、マニュアルオーバーライドを使用して強制的に そのコーディネータおよびそれ以外のすべてのパーティシパントの トランザクションを解決する必要があります。 また、マニュアルオーバライドを行う場合には、関係のあるすべてのデータベースに 対して必ず一貫した方法で対応しなければなりません。

マニュアルオーバーライドは、問い合わせ言語コマンドを使用する場合に効果を発揮します。 これらのオプションは、そのトランザクションに関係のあるサイト上でのみ 使用することができますが、必ず十分な注意して使用しなくてはなりません。 マニュアルオーバーライドの不注意な使用、あるいは無責任な使用は、 結果としてデータベース上のデータの一貫性を失うことになり、 矛盾したデータになる可能性があります。 これはパーティシパントの強制コミットまたは強制ロールバックが コーディネータまたはそれ以外のパーティシパントの復帰手続きと 矛盾する可能性があるためです。

以下のコマンドはトランザクションを識別し、そのステータスを表示し、 トランザクションの復元または解決するために使用します。 これらのコマンドはカレントのトランザクション上にない場合に限り使用できます。

表 3-2: トランザクション復元コマンド

コマンド | 説明
`DISPLAY WORK ON ...` | データベースに関係のある処理中(つまり、完了していない状態) のすべてのトランザクションの表示。
`DISPLAY WORK id;` | トランザクションステータスの表示
`START WORK id FROM ...` | セーブポイントまでロールバックすることによるトランザクションの復元
`ROLLBACK WORK id;` | トランザクションの強制ロールバック
`COMMIT WORK id;` | トランザクションの強制コミット

### 3.11.10 セーブポイントへの復帰

保護されていないトランザクションが コミットコマンドの実行およびログ出力される前に中断される場合、 自動ウォームリスタートユーティリティがそのトランザクションを ロールバックします。 トランザクション全体の消失を防ぐには、 トランザクション内でセーブポイントを使用して、 完了したトランザクションを部分的に復元する手段をとることが可能です。 トランザクション中にセーブポイントを設定することによって、 中断後の任意のセーブポイントまでトランザクションを復元することができます。 従って、セーブポイントまで復元することによってトランザクションを解決します。 セーブポイントまでトランザクションを復元した場合には、 そのトランザクションは後のウォームリスタートユーティリティの実行によって 影響されることはありません。

トランザクションが保護されていない場合、 セーブポイントまでの復元は必ずウォームリスタートユーティリティの実行の 前に行わなければなりません。 トランザクションが保護されている場合、 トランザクションがセーブポイントまで 復元可能であれば、 （それとともにプロテクションをオーバーライドするための `-p`オプションを使用していなければ） ウォームリスタートユーティリティはそのトランザクションを無視します。 従って、ウォームリスタートユーティリティの実行後に、 セーブポイントまでの復元が可能となります。

セーブポイントは、トランザクション中に次のコマンドで設定します。

       SAVEPOINT savepoint;

`savepoint` はセーブポイント名であり、レター文字で始まる 文字列でなければなりません。 また、セーブポイントは現在のトランザクション内でネストしたトランザクションを 開始するためにも使用されます。

以下のコマンドを指定することでトランザクションは セーブポイントまで復元されます。

       START WORK [database:]id [FROM savepoint];

`database`は、データベース名になります。 データベースを指定しない場合、データベースは、 現在のデフォルトデータベースと仮定されます。 各トランザクションはそれぞれIDを持ち、 このIDは、以降で説明する`DISPLAY WORK`コマンドで トランザクションを特定するために使用されます。 セーブポイントを指定しなければ、最後に定義したセーブポイントまで トランザクションは復元されます。 セーブポイントのないトランザクションの場合、 このコマンドを使用しても復元することはできません。

トランザクション中ではない場合でもこのコマンドを使用することができます。 適切なセーブポイントの箇所でトランザクションが再スタートされ、 その時点からトランザクションを続けることができます。

### 3.11.11 トランザクション保護

トランザクションを自動ウォームリスタートユーティリティから保護することができます。 システム変数`MSTRANSWARMPROTECT`は、 トランザクションを自動ウォームリスタートユーティリティ`empwarm`で 処理（解決）するかどうかを制御するフラグです。 トランザクションの開始時にこの変数に任意の値が設定されていれば、 そのトランザクションはウォームリスタートユーティリティから保護されます。 つまり、`empwarm`ユーティリティは、セーブポイントまでトランザクションが 回復できるのであれば、そのトランザクションを無視します。 この機能は、自動ウォームリスタートがシステムのリブート処理の 一部に含まれている場合に役に立ちます。 コミットコマンドが実行されない限り、保護されていないトランザクションは 確実にロールバックするため、同様に中断された保護されていない冗長な トランザクションについてもロールバックの試みられます。 トランザクションを保護することによって、 ウォームリスタートユーティリティの実行後に `START WORK FROM savepoint`コマンドを使用して セーブポイントまでトランザクションを復元することが可能です。

この変数はデフォルトでは設定されていませんので、 ウォームリスタートユーティリティはすべての中断されたトランザクションを デフォルトで解決します。変数を設定するためには以下のように入力します。

       SET MSTRANSWARMPROTECT TO TRUE;

## 3.12 ロックとテーブルアクセス

ロックは矛盾したアクセスを防ぎ、 テーブルへの並行したアクセスを制御することによって データの整合性を維持します。 常にEmpressは、テーブルに関するコマンドを受け付けた場合に シェアードメモリに存在するか、あるいはディスク上のファイル内の どちらかに格納されているテーブルのロックステータスの判断を試みます。 また、ロックレベルがチェックされている間は 他のEmpressプロセスがロックマネージャに アクセスすることはありません。

更新処理中である場合、ロックマネージャは他の操作を開始させません。 コマンドを処理することが可能な場合は、ファイルにその操作の種類のマークをつけて、 継続します。

既にテーブルがロックされている場合はアクセスすることはできず、 この場合、リトライを繰り返します。

そのリトライの回数およびリトライ間のインターバルは、 Empressシステム変数、 `MSEXCLRETRY`、 `MSIAEXCLSLEEP`、 `MSLOCKRETRY`、 `MSLOCKSLEEP`、 `MSIAEXCLRETRY`、 `MSIAEXCLSLEEP`、 `MSIALOCKRETRY`、 `MSIALOCKSLEEP` によって制御されます。 これらの変数は、 ロックのリトライによるデッドロック(システムの"ハングアップ"のように見える) を防ぐための十分な制御をすることを目的として 提供されています。

`MSEXCLRETRY`は、Empressが ロックファイルを読むためのリトライ回数を決定します。 デフォルトでは 30 回です。 `MSEXCLSLEEP`は、 リトライ間のインターバル秒数を決定します。 デフォルトでは 1 秒です。 SQLセッション中で設定する場合は以下のように指定します。

       SET MSEXCLRETRY TO 5;
       SET MSEXCLSLEEP TO 2;

上記の設定では、ロックファイルを読むためのリトライ数は 5回で、 そのリトライの間のインターバルとして 2 秒間の設定になります。 リトライをあきらめるまで合計で8 秒間費やされます。

`MSLOCKRETRY`は、ロックファイルへのアクセスができ、 そのロックを書くためにリトライする回数を決定します。 例えば、他のコマンドのアクセスを防ぐためにロックがかかれ、 更新モードでテーブルがロックされている可能性があります。 デフォルトでは 30 回です。

`MSLOCKSLEEP`は、このリトライのインターバル秒数です。 デフォルトでは　1 秒です。 SQLセッション中で設定する場合は以下のように指定します。

       SET MSLOCKRETRY TO 5;
       SET MSLOCKSLEEP TO 2;

上記の設定では、ロックファイルを書くためのリトライ数は 5回で、 そのリトライの間のインターバルとして 2 秒間の設定になります。 リトライをあきらめるまで合計で8 秒間費やされます。

`MSIAEXCLRETRY`は、対話型インターフェイスを使用した場合の ロックを読むためのリトライ回数を決定します。 デフォルトでは 2 回です。 `MSIAEXCLSLEEP`は、このリトライのインターバル秒数です。 デフォルトでは　1 秒です。 SQLセッション中で設定する場合は以下のように指定します。

       SET MSIAEXCLRETRY TO 3;
       SET MSIAEXCLSLEEP TO 3;

上記の設定では、対話型インターフェイスを使用しテーブルにアクセスする リトライ数は 3回で、そのリトライの間のインターバルとして 3 秒間の設定になります。 リトライをあきらめるまで合計で6 秒間費やされます。

`MSIAEXCLRETRY`は、対話型インターフェイスを使用した場合の ロックを書くためのリトライ回数を決定します。この設定は、 `MSLOCKRETRY`の設定を上書きします。 デフォルトでは 2 回です。 `MSIALOCKSLEEP`は、このリトライのインターバル秒数です。 デフォルトでは　1 秒です。 SQLセッション中で設定する場合は以下のように指定します。

       SET MSIALOCKRETRY TO 3;
       SET MSIALOCKSLEEP TO 3;

上記の設定では、ロックファイルを書くためのリトライ数は 3回で、 そのリトライの間のインターバルとして 3 秒間の設定になります。 リトライをあきらめるまで合計で6 秒間費やされます。