このためにシーケンスを使用することはできません。 すべてを通過する単一のシリアル化ポイントが必要です 挿入を行う必要があります。そうしないと、「ギャップレス」属性が保証されません。また、そのテーブルから行が削除されないようにする必要があります。
シリアル化は、単一のトランザクションのみがそのテーブルに行を挿入できることも意味します。他のすべての挿入は、「前の」挿入がコミットまたはロールバックされるまで待機する必要があります。
これを実装する方法の1つのパターンは、「シーケンス」番号が格納されているテーブルを作成することです。法的な理由でギャップレスでなければならない請求書番号にこれが必要であると仮定しましょう。
したがって、最初に「現在の値」を保持するテーブルを作成します。
create table slow_sequence
(
seq_name varchar(100) not null primary key,
current_value integer not null default 0
);
-- create a "sequence" for invoices
insert into slow_sequence values ('invoice');
次に、次の番号を生成する関数が必要ですが、2つのトランザクションが同時に次の番号を取得できないことを保証します。
create or replace function next_number(p_seq_name text)
returns integer
as
$$
update slow_sequence
set current_value = current_value + 1
where seq_name = p_seq_name
returning current_value;
$$
language sql;
この関数はカウンターをインクリメントし、結果としてインクリメントされた値を返します。 update
のため これでシーケンスの行がロックされ、他のトランザクションはその値を更新できなくなります。呼び出し元のトランザクションがロールバックされると、シーケンスカウンターの更新もロールバックされます。コミットされると、新しい値が保持されます。
すべてを確実にするため トランザクションは関数を使用します。トリガーを作成する必要があります。
問題のテーブルを作成します:
create table invoice
(
invoice_number integer not null primary key,
customer_id integer not null,
due_date date not null
);
次に、トリガー関数とトリガーを作成します。
create or replace function f_invoice_trigger()
returns trigger
as
$$
begin
-- the number is assigned unconditionally so that this can't
-- be prevented by supplying a specific number
new.invoice_number := next_number('invoice');
return new;
end;
$$
language plpgsql;
create trigger invoice_trigger
before insert on invoice
for each row
execute procedure f_invoice_trigger();
1つのトランザクションがこれを行う場合:
insert into invoice (customer_id, due_date)
values (42, date '2015-12-01');
新しい番号が生成されます。 秒 次に、トランザクションは最初の挿入がコミットまたはロールバックされるまで待機する必要があります。
私が言ったように、このソリューションはスケーラブルではありません。全くない。そのテーブルに多くの挿入がある場合、アプリケーションの速度が大幅に低下します。ただし、スケーラブルなとの両方を使用することはできません。 ギャップレスシーケンスの正しい実装。
また、上記のコードでカバーされていないエッジケースがあることもかなり確信しています。そのため、まだギャップが生じる可能性があります。