SELECT文で取得したデータを更新するために、Oracleはテーブルのキー、特にユニークキーを必要とします。

Oracleは全てのテーブルで使えるユニークキーとして
ROWIDを用意するため、TOracleDataSetは、データセットを更新するために、テーブルの
ROWIDを使うように設計されています。

よって、SQLプロパティのSELECT文に、テーブルのROWIDを含めることが、データセットを更新可能にするために必要です。ROWIDがクエリに含まれない場合データセットはリードオンリーになってしまいます。なお、SELECT対象がリードオンリーのビューであるなど、ROWIDを使用した更新が不可能な場合にも、OnApplyRecordイベントを記述することで対応が可能です(後述)。

ROWIDをクエリに含めさえすれば、BDEのTQueryと同じように以下のメンバを用いてデータを操作できます。

  • TOracleDataSet : データを更新する上で使うメンバ =
    TDataSet互換メンバ

    • property
      State:
      データセットの状態を返します。開かれたデータセットの安定した状態である、dsBrowse /
      dsEdit / dsInsert の三状態の判別のために利用されることが多いでしょう。
    • procedure
      Append/
      Insert :
      データセットにレコードを追加します。AppendとInsertではデータセット内での追加位置が異なりDBグリッドなどで表示される位置に違いがありますが、Oracleデータベースはキー項目以外に位置情報を持たないので、結局は同じ効果です。
    • procedure
      Edit : データを編集状態にします
    • procedure
      Post : 編集内容を確定させます
    • procedure
      Cancel : 編集をキャンセルします

ROWIDをプログラムで使いたい場合

上記で述べた理由で、SELECT文の中で見つけたROWIDを、データセット更新のために内部で利用します。そのため、以下のように、プログラムの中でROWIDを使いたい場合には、問題が発生してしまいます。

  //OracleDataSet.SQL = SELECT ROWID, ENAME FROM EMP
  procedure TForm1.Button1Click(Sender: TObject);
  begin
    with OracleDataSet1 do begin
      Open;
      ShowMessage('ROWID of the first record = ' + FieldByName('ROWID').AsString);
    end;
  end;

というのも、OracleDataSetが
ROWIDを内部利用のために取ってしまうため、アプリケーションプログラムからアクセス可能なフィールドにはROWIDが含まれず、FieldByName(
ROWID)によるROWIDの参照がエラーとなってしまうのです。したがって、プログラムでROWIDの値を利用するには、以下のように、SQLのSELECT文において、ROWIDを二重に取得する必要があります。

  SELECT ROWID, ROWID AS ROW_ID, ENAME FROM EMP

このようにすれば、ROWIDはDOAの内部処理に取られても、ROW_IDにはプログラムからアクセスできるようになるのです。

テーブルの更新に関する注意点

  1. テーブルの更新については、以下の二点に注意してください。
  2. DOAは、SELECT文のFROM句において最初に指定されたテーブルを更新対象のテーブルと判別します。複数のテーブルを結合(JOIN)させたSELECT文において、FROM句の右に複数のテーブル名が列挙されますが、2番目以下のテーブルは無視されます。

    1. DOAは、データを更新するにあたり、「UPDATE Table名 SET カラム名=値 WHERE ROWID=**
      」の形式でUPDATE文を作成して実行します。 このため、カラム名
      はテーブル上に実在**するカラム名と一致する必要があります。すなわち、SELECT文において、更新対象のテーブルのカラムには、別名を与えてはいけません。("SELECT
      EMPNO AS ENO ,ROWID FROM EMP"などは悪い例です)
    2. なお、SQL文中でCOUNT(*), SUMなどの関数を用いて
      計算されたフィールドに正しくアクセスするには別名が必要です。これらのフィールドのTField.
      FieldKind
      fkInternalCalcにセットして、更新時に編集を破棄しなければなりません。おそらく、TField.
      ReadOnlyもTrueにセットすべきでしょう。
  • TOracleDataSet * property
    UpdatingTable : string :
    更新対象のテーブル名を明示的に与える場合にセットします。もとのSELECT文においてJOINが使われた場合か、データセットがカーソル変数に関連付けられた場合(後述)です。

OnApplyRecordイベント

ROWIDを用いて素直にテーブルのアクセスができない、以下のような場合に、OnApplyRecordイベントハンドラを記述することで対応が可能です。

  1. データベースユーザに、テーブルを更新する権限がなく、特定のストアドプロシージャ(テーブルAPI)を通じて更新しなければならない場合
  2. 読み取り専用のビューからSELECTしたために、ROWIDが取得できず、直接テーブルを更新できない場合
  3. データセットからレコードを削除する際に、テーブル上では論理削除フラグをセットするにとどめたい場合

OnApplyRecordイベントのハンドラでは、DOAが内部的にテーブルをチェックしたり、ロックしたりする場合を含め、幾つかのアクセスパターンについて、ROWIDによらずに特定の1レコードにアクセスする方法を記述する必要があります。詳しくはDOAのヘルプを参照してください。テーブルAPIの開発効率を上げるためのヘルパー関数なども用意されています。