Anda tidak dapat menggunakan urutan untuk ini. Anda memerlukan satu titik serialisasi yang melaluinya semua sisipan harus pergi - jika tidak, atribut "tanpa celah" tidak dapat dijamin. Anda juga perlu memastikan bahwa tidak ada baris yang akan dihapus dari tabel itu.
Serialisasi juga berarti bahwa hanya satu transaksi yang dapat menyisipkan baris ke dalam tabel itu - semua sisipan lainnya harus menunggu hingga sisipan "sebelumnya" telah dikomit atau dibatalkan.
Salah satu pola bagaimana ini dapat diimplementasikan adalah memiliki tabel tempat nomor "urutan" disimpan. Mari kita asumsikan kita membutuhkan ini untuk nomor faktur yang harus tanpa celah karena alasan hukum.
Jadi pertama-tama kita buat tabel untuk menampung "nilai saat ini":
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');
Sekarang kita membutuhkan fungsi yang akan menghasilkan nomor berikutnya tetapi itu menjamin bahwa tidak ada dua transaksi yang dapat memperoleh nomor berikutnya secara bersamaan.
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;
Fungsi akan menambah penghitung dan mengembalikan nilai yang bertambah sebagai hasilnya. Karena update
baris untuk urutan sekarang terkunci dan tidak ada transaksi lain yang dapat memperbarui nilai itu. Jika transaksi panggilan dibatalkan, begitu juga pembaruan ke penghitung urutan. Jika di-commit, nilai baru akan tetap ada.
Untuk memastikan bahwa setiap transaksi menggunakan fungsi, pemicu harus dibuat.
Buat tabel yang dimaksud:
create table invoice
(
invoice_number integer not null primary key,
customer_id integer not null,
due_date date not null
);
Sekarang buat fungsi pemicu dan pemicu:
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();
Sekarang jika satu transaksi melakukan ini:
insert into invoice (customer_id, due_date)
values (42, date '2015-12-01');
Nomor baru dihasilkan. detik transaksi kemudian perlu menunggu hingga penyisipan pertama dilakukan atau dibatalkan.
Seperti yang saya katakan:solusi ini tidak terukur. Tidak semuanya. Ini akan memperlambat aplikasi Anda secara besar-besaran jika ada banyak sisipan ke dalam tabel itu. Tetapi Anda tidak dapat memiliki keduanya:dan . yang skalabel implementasi yang benar dari urutan tanpa celah.
Saya juga cukup yakin bahwa ada kasus tepi yang tidak tercakup oleh kode di atas. Jadi kemungkinan besar Anda masih bisa menemukan celah.