最近は新たに表を作る時は、もっぱらクイックSQLを使っています。こういう使い方をした方が便利だな、と感じたことをまとめてみます。
いきなり仕様を読むのもつらいので、その前段階の使い方の紹介になります。仕様そのものについては、オンライン・ヘルプを参照してください。
クイックSQLが使えるサイト
Oracle APEXがインストールされていれば、どこでも。SQLワークショップに含まれるユーティリティを開いて、クイックSQLを実行します。
LiveSQL - https://livesql.oracle.com - でも、クイックSQLを実行できます。
コメントによるインライン設定
モデルの保存
表定義の基本
# date: timestamp with local time zone
customers
    name vc80
    register_date
    age num# date: timestamp with local time zone列の制約の指定
    is_enabled vc1 /check Y,N /default N /nn    is_enabled                     varchar2(1) default 'N' constraint customers_is_enabled_cc
                                   check (is_enabled in ('Y','N')) not null,    is_enabled vc1 /nn /default N /check Y,N    is_enabled                     varchar2(1) default 'N' constraint customers_is_enabled_vc_cc
                                   check (is_enabled_vc1__/default_n in ('Y','N')) not null,マスター・ディテイル関係の表の定義
# semantics: default
orders
    customer_name
    order_date
    order_items
        product_name
        price num
        quantity num-- create tables
create table orders (
    id                             number generated by default on null as identity 
                                   constraint orders_id_pk primary key,
    customer_name                  varchar2(255),
    order_date                     date
)
;
create table order_items (
    id                             number generated by default on null as identity 
                                   constraint order_items_id_pk primary key,
    order_id                       number
                                   constraint order_items_order_id_fk
                                   references orders on delete cascade,
    product_name                   varchar2(255),
    price                          number,
    quantity                       number
)
;
-- table index
create index order_items_i1 on order_items (order_id);主キー定義
customers
    customer_id num /pk
    name vc80
    register_date
    age num# prefixPKwithTname: true
# PK: identity
# DB: 18c
customers
    name vc80
    register_date
    age num    customer_id                    number default on null to_number(sys_guid(), 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') 
                                   constraint customers_id_pk primary key,    customer_id                    number default on null customers_seq.NEXTVAL 
                                   constraint customers_id_pk primary key,    customer_id                    number generated by default on null as identity 
                                   constraint customers_id_pk primary key,外部キー制約
 # semantics: default
orders
    customer_name
    order_date
order_items
    order_id /fk orders
    product_name
    price num
    quantity num監査列について
    created                        date not null,
    created_by                     varchar2(255) not null,
    updated                        date not null,
    updated_by                     varchar2(255) not null-- triggers
create or replace trigger orders_biu
    before insert or update 
    on orders
    for each row
begin
    if inserting then
        :new.created := sysdate;
        :new.created_by := coalesce(sys_context('APEX$SESSION','APP_USER'),user);
    end if;
    :new.updated := sysdate;
    :new.updated_by := coalesce(sys_context('APEX$SESSION','APP_USER'),user);
end orders_biu;
/-- triggers
create or replace trigger orders_biu
    before insert or update 
    on orders
    for each row
begin
    if inserting then
        :new.created := sysdate;
        :new.created_by := user;
    end if;
    :new.updated := sysdate;
    :new.updated_by := user;
end orders_biu;
/表と列の名前について
# prefix: tst
# semantics: default
emploees
   name vc80
   sal num
   com num-- create tables
create table tst_emploees (
    id                             number generated by default on null as identity 
                                   constraint tst_emploees_id_pk primary key,
    name                           varchar2(80),
    sal                            number,
    com                            number
)
;# prefixPKwithTname: true
# prefix: tst
# semantics: default
emploees
   employee_name vc80
   sal num-- create tables
create table tst_emploees (
    tst_emploee_id                 number generated by default on null as identity 
                                   constraint tst_emploees_id_pk primary key,
    employee_name                  varchar2(80),
    sal                            number
)
;行キーと行バージョン番号
# prefix: tst
# semantics: default
# rowVersion: true
emploees
   employee_name vc80
   sal num列ROW_VERSIONが追加され、その列に値を設定するトリガーが生成されます。
-- create tables
create table tst_emploees (
    id                             number generated by default on null as identity 
                                   constraint tst_emploees_id_pk primary key,
    row_version                    integer not null,
    employee_name                  varchar2(80),
    sal                            number
)
;
-- triggers
create or replace trigger tst_emploees_biu
    before insert or update 
    on tst_emploees
    for each row
begin
    if inserting then
        :new.row_version := 1;
    elsif updating then
        :new.row_version := nvl(:old.row_version,0) + 1;
    end if;
end tst_emploees_biu;
/行バージョン番号は、その行の更新が発生する度に1づつインクリメントされます。この列の使いどころの説明として、REST APIでの利用を考えてみます。
最初に、表TST_EMPLOYEESよりIDを渡して、EMPLOYEE_NAME, SALを取得します。このときROW_VERSIONも同時に取得します。
1. クライアントAがID 300のデータを取得しました。
Client A: https://xxxxxx/xxx/employee/300 (GET)
取得した値は以下です。
{ "id": 300, "employee_name": "山田太郎", "sal": 300, "row_version": 4 }
2. クライアントBも同じデータを取得しました。
Client B: https://xxxxxx/xxx/employee/300 (GET)
{ "id": 300, "employee_name": "山田太郎", "sal": 300, "row_version": 4 }
表に保存されている行のROW_VERSIONはすでに5になっており、送信されたデータとは異なるため更新は失敗します。
以上のように行バージョン番号は、異なるクライアントによって、アップデートが不用意に上書きされないことを保証するために使用されます。
HTTPのPUTを受け付けて列SALを更新する前に、ROW_VERSIONの値を参照する必要があります。そのため、以下のような問い合わせを発行します。
select row_version from tst_employees where id = 300 for update;
受信したrow_versionと比較して、一致していたら、
update tst_employees set sal = 300 where id = 300;
といった手順を踏むことになります。ここで主キー列の名称が表毎に異なっていると、row_versionを取り出す手順が煩雑になります。一意列が同じ列名ROW_KEYとして全ての表に定義されていることにより、少々扱いが容易になります。これが行キーの使い道です。
私の嗜好としては、主キーに表名を接頭辞として付けますがOFFで、主キー列がつねにIDという名前になっていれば、列ROW_KEYを定義する理由はないので、定義しないです。
Oracle APEXでは行バージョン番号の代わりにチェックサムを使うことで、上記の制御を実装しています。以下のような形式でチェックサムを計算し、保存されている行のチェックサムと受信したチェックサムに違いが無ければ更新処理を行います。
SELECT APEX_ITEM.MD5_CHECKSUM(ID, EMPLOYEE_NAME, SAL) FROM TST_EMPLOYEES WHERE ID = 300 FOR UPDATE NOWAIT;行バージョン番号も使用できますが、デフォルトではチェックサムを使用します。そのため、Oracle APEXで表を扱う範囲では、列キーと行バージョン列の双方とも必要としていません。
チェックサムでは扱いが難しい、例えば表に列が大量に定義されている、Oracle APEXの外でデータが変更されることがある、といった場合に利用を検討します。
表と列のコメント
employees -- 従業員の情報
   employee_name vc80  -- 従業員名
   sal num  -- 月給または
employees [従業員の情報]
   employee_name vc80  [従業員名]
   sal num  [月給]-- comments
comment on table emploees is '従業員の情報';
comment on column emploees.employee_name is '従業員名';
comment on column emploees.sal is '月給';テスト・データの生成
# language: "JA"
履歴表の作成
# prefix: tst
employees /history
   name vc80
   sal numデフォルトのセマンティクス
# semantics: default













