web-dev-qa-db-ja.com

Oracle SQLでトリガーとシーケンスを使用するカスタムシーケンス

すべての親アイテムが挿入された後にカスタムシーケンスを挿入するトリガーがあります。これで、親アイテムIDを介して親アイテムにリンクされたサブアイテムがあり(シーケンスと親アイテムIDは2つの異なる列です)、サブアイテムは1を超える場合があります。親アイテムのトリガーによってシーケンスが「P2505」として生成され、サブアイテムシーケンスは「P2505-1」、2番目のサブアイテムは「P2505-2」のようにする必要があります。

次の親アイテムのシーケンスが「P2506」の場合、サブアイテムシーケンスは「P2506-1」のようになります。

これは、親アイテムのシーケンスを生成するためのトリガーです

create or replace trigger REF_NO_TRIGGER
    before insert on LOST_ITEM
    for each row 
declare 
    PREFIX varchar2(254); 
begin
    select NAME into PREFIX
        from SUBCAT
        where SUBCAT.ID = :new.SUBCAT_ID; 
    :new.SEQUENCE := substr(PREFIX,1,1)
      ||(:new.CATEGORY_ID) 
      ||(:new.SUBCAT_ID) 
      ||(:new.ID); 
    end;

親アイテムテーブル

ID  NUMBER(15,0)
CATEGORY_ID NUMBER(3,0)
SUBCAT_ID    NUMBER(5,0)
NAME VARCHAR2(20)
SEQUENCE  VARCHAR2(22 BYTE)

サブアイテムテーブル

ID  NUMBER(30,0)
PARENT_ITEM_ID  NUMBER(15,0)
NAME VARCHAR2(20)
SEQUENCE    VARCHAR2(22 BYTE)

親とサブアイテムのテーブルを作成する

CREATE TABLE PARENT_ITEM(ID NUMBER(15) GENERATED BY DEFAULT AS IDENTITY (START WITH 1 INCREMENT BY 1),
CATEGORY_ID NUMBER(3) NOT NULL,
SUBCAT_ID NUMBER(5) NOT NULL,
NAME VARCHAR2(20) NOT NULL,
SEQUENCE  VARCHAR2(22),
PRIMARY KEY (ID)
);




CREATE TABLE SUB_ITEM(ID NUMBER(30) GENERATED BY DEFAULT AS IDENTITY (START WITH 1 INCREMENT BY 1),
PARENT_ITEM_ID NUMBER(15) NOT NULL,
NAME VARCHAR2(20),
 SEQUENCE VARCHAR2(22 BYTE),
CONSTRAINT FK_LOST_IT_SUB_LOST_IT FOREIGN KEY (PARENT_ITEM_ID) REFERENCES PARENT_ITEM(ID),
PRIMARY KEY (ID)
);

INSERT INTO PARENT_ITEM(CATEGORY_ID, SUBCAT_ID, NAME) VALUES (2,5,'MAIN_ITEM');

ID IS GENERATEDが自動的に生成され、トリガーによって親アイテムのシーケンスが生成されます。サブアイテムの場合、名前と親アイテムIDのみを挿入するので、そのためのシーケンスが必要です。

私の理解に従って、サブアイテムテーブルで親アイテムシーケンスを確認し、エントリがあるかどうかを確認する必要があります。そのシーケンスにエントリがない場合、最初のサブアイテムは「ParentItemSequence-1」である必要があります。 「-2」、「-3」などを数えて追加

しかし、親とサブアイテムを含むすべてのアイテムを同時に送信する必要があるため、トリガーを介してこの機能を実現する方法がわかりません。

1
Pankaj Singh

ギャップフリーの連番

探しているものは、「ギャップのない連続番号」と呼ばれます。

これらのタイプのビジネスリクエストは、関連するテーブルのパフォーマンスを低下させることが知られています。これは、番号を生成するエンティティで必要なシリアル化が原因です。

親テーブル

PARENT_ITEM.IDは「ギャップのない連番」にはなりません。 NOCACHEオプションがありません。

それでも、まだギャップがある可能性があります。

子テーブル

CHILD_ITEM.IDを代理キーとして保持することをお勧めします。 Identity列はデフォルトのままにします。また、「-1」を表す数値の別の列が必要です。この列をCHILD_NUMBER_SEQUENCEと呼びます。

ここで、CHILD_SEQUENCEの「ナンバジェネレータ」をシリアル化するには、PARENT_ITEM_IDに基づくロックを取得する必要があります。

Oracleの場合、最も簡単な方法は親行をロックすることです。テーブルPARENT_ITEMが「現在の子シーケンス番号」を保持している場合、「親行をロックする」と同時に「次の値を取得する」ことができるため、アルゴリズムは簡単になります。

テーブルの変更

alter table parent_item add ( current_child_sequence_number number(5) default 0);
alter table child_item add (child_sequence_number number(5) not null);
alter table child_item add
  constraint child_item_uq1 unique (parent_item_id, child_sequence_number);

「CHILD_ITEM.CHILD_SEQUENCE_NUMBER」を生成するコード

-- this locks and updates the parent row.
update parent_item p
set p.current_child_sequence_number = nvl(p.current_child_sequence_number,0) + 1
where p.id = :new.parent_item_id
returning p.current_child_sequence_number into :new.child_sequence_number;
-- make sure you check that 1 and only 1 row was updated.
-- fail if that isn't true.

最終メモ

コードは、単一行の処理に固有です。

一括読み込みのパフォーマンスが必要な場合は、ステージングテーブル(グローバル一時テーブルなど)とプロシージャを使用する必要があります。

繰り返しになりますが、「ギャップのない連番」のビジネス要件は本質的に遅いです。それが高速であると期待しないでください。

1
Michael Kutz