SEQUENCEによるAutoIncカラムの実現
ORACLEのテーブルのカラム型には、いわゆるAutoInc型やAutoNumber型のように、新規レコードを挿入すると自動的にインクリメントされてユニークになることが保証されるカラム型というものはありません。
かわりに、SEQUENCEというカウンタ様の仕組みを利用して、新しいキー値を取得して使います。
CREATE SEQUENCE MySeq START WITH 1 INCREMENT BY 1;
のようにして作成したシーケンスを使って、新しいキーを取得してデータを挿入するには、このようにして、シーケンスのカウントアップを行います。
INSERT INTO MyTable VALUES (MySeq.NextVal, Data1, Data2 ... )
あるいは、以下のように、シーケンスのカウントアップと挿入を2段階に分けて実行します。
DECLARE NewKey NUMBER(10); BEGIN SELECT MySeq.Nextval INTO NewKey FROM dual; INSERT INTO TESTINS VALUES(NewKey, 'AA'); END;
後者の方法は、Master/Detail関係にある複数テーブルに一度に新規レコードを追加する場合などにとられます。たとえばこんな風に…
DECLARE NewKey NUMBER(10); BEGIN SELECT MySeq.Nextval INTO NewKey FROM dual; INSERT INTO MASTER VALUES(NewKey, 'AA'); INSERT INTO DETAIL VALUES(NewKey, 0, 'CCD', 'EEE'); INSERT INTO DETAIL VALUES(NewKey, 1, 'CCF', 'GGG'); END;
TOracleDataSetを用いてデータを挿入する際に、SequenceFieldプロパティをセットしておくと、テーブルの特定カラムの値を、シーケンスから取得するようにできます。
- TOracleDataSet
- property
SequenceField :
新規レコードの特定のカラムに、シーケンスから値を取得するよう設定します- Sequence: シーケンスの名称
- Field : シーケンスから取得した値をセットすべきフィールド名
-
ApplyMoment: シーケンスからのキー取得のタイミング。
以下の3つのいずれかです。- On New Record : Append/Insert のタイミングでNextValを取得します
- On Post : レコードを PostするタイミングでNextValを取得します
- On Server : NextValは、INSERT文/UPDATE文の一部として実行されます。
- property
たとえば、TOracleDataSet.SQL = SELECT EMPNO, ENAME, ...FROM EMP のとき、Sequence = MySeq,
Field=EMPNOの場合、
- ApplyMoment = On New Record だとすると、OracleDataSet1.Append; によってSELECT MySeq.NextVal INTO ***
が実行されて、その結果が即座にDataSetにセットされます。この場合、データセットの更新をCancelしても、シーケンスの値は元に戻らないので、カウンタの値を一つ無駄にしてしまうことになります。 - ApplyMoment = On Post
だと、Appendの時点でDataSetのEMPNOフィールドはNULLのままで、ENAMEプロパティをセットしてPostするタイミングで、まずSELECT
MySeq.NextVal INTO *
が実行され、ついでその結果のキー値をDataSetのEMPNOフィールドにセットしてから、INSERT文が実行されます。(このApplyMoment=OnPost**が基本と考えていいでしょう) - ApplyMoment = On Server の場合、Append/ Post
の時点で、データセットのEMPNOフィールドはNULLのまま、INSERT INTO EMP VALUES( MySeq.NextVal, ENAME,
....)
が実行されます。これによって、データベース上のテーブルには新しいキー値を持つ有効なレコードが作成されますが、このままではローカルのTOracleDataSet上のレコードのEMPNOはNULLのままです。そのため、TOracleDataSetの
RefreshOptionsプロパティを適切にセットして、データの更新後にレコードの再読込を行わせる必要があります。なお、RefreshOptions.roAfterInsertがセットされている場合には、この方法が効率的です。
デフォルト値およびトリガーによる更新に関する考慮
テーブルのカラムはデフォルト値が設定できます。たとえば
create table TestTable ( KEY number not null, DATA number default 0 not null );
のようにして作成したテーブルにデータを挿入する際、DATAフィールドの値が与えられなかった場合、デフォルト値の0がテーブルにセットされます。
また、このようにトリガーが設定されていると、テーブルデータを更新する都度、トリガーが働いてデータが更新されます。
create or replace trigger MyTrigger before update on testtable for each row begin Update testtable Set data = data + 1; end MyTrigger;
これらの機能はデータの整合性を保つために有用ですが、サーバ上でデータが自動的に更新されることによって、クライアントのTOracleDataSetが保持しているデータレコードと、サーバ上のレコードのデータ間に不一致が生ずる原因となりえます。この不一致を解消するためには、随時サーバからデータを読み込みなおす必要があります。このためにRefreshOptionsプロパティを使います。
-
TOracleQuery
- property
RefreshOptions:
サーバデータ読み込みのタイミングを設定します。以下の4つのタイミングの組み合わせです-
roAfterInsert :
INSERT終了後に、サーバからデータを読み直し、デフォルト値やトリガーの結果を反映します -
roAfterUpdate :
UPDATE終了後に、サーバからデータを読み直し、デフォルト値やトリガーの結果を反映します -
roBeforeEdit :
他のユーザがレコードを更新していれば、その変更を編集直前に再読込します。後述の、レコードロックに関する説明を参考にしてください。 -
roAllFields : TRUEの場合JOINで結合された
他のテーブルのレコードや、計算項目も読み直します。この際、もとのSQL文のWHERE句に
rowid = :rowid
を加えたクエリが実行されますが、もとのWHERE句の条件によってはこのクエリにヒットするレコードが0件になってしまう問題があるため、工夫が必要になることがあります。詳しくはTOracleQuery.RefreshOptionsのヘルプを参照してください。
-
roAfterInsert :
- property