Sejak seseorang memposting pertanyaan serupa, saya telah merenungkan ini. Masalah pertama adalah bahwa DB tidak menyediakan urutan "yang dapat dipartisi" (yang akan dimulai ulang/diingat berdasarkan kunci yang berbeda). Yang kedua adalah SEQUENCE
objek yang adalah yang disediakan ditujukan untuk akses cepat, dan tidak dapat dibatalkan (yaitu, Anda akan mendapatkan celah). Ini pada dasarnya mengesampingkan penggunaan utilitas bawaan... artinya kita harus menjalankannya sendiri.
Hal pertama yang kita perlukan adalah tabel untuk menyimpan nomor urut kita. Ini bisa sangat sederhana:
CREATE TABLE Invoice_Sequence (base CHAR(1) PRIMARY KEY CLUSTERED,
invoiceNumber INTEGER);
Pada kenyataannya base
kolom harus menjadi referensi kunci asing ke tabel/id apa pun yang mendefinisikan bisnis/entitas tempat Anda menerbitkan faktur. Dalam tabel ini, Anda ingin entri menjadi unik per entitas yang diterbitkan.
Selanjutnya, Anda menginginkan proc tersimpan yang akan mengambil kunci (base
) dan keluarkan nomor berikutnya dalam urutan (invoiceNumber
). Kumpulan kunci yang diperlukan akan bervariasi (yaitu, beberapa nomor faktur harus berisi tahun atau tanggal lengkap penerbitan), tetapi bentuk dasar untuk situasi ini adalah sebagai berikut:
CREATE PROCEDURE Next_Invoice_Number @baseKey CHAR(1),
@invoiceNumber INTEGER OUTPUT
AS MERGE INTO Invoice_Sequence Stored
USING (VALUES (@baseKey)) Incoming(base)
ON Incoming.base = Stored.base
WHEN MATCHED THEN UPDATE SET Stored.invoiceNumber = Stored.invoiceNumber + 1
WHEN NOT MATCHED BY TARGET THEN INSERT (base) VALUES(@baseKey)
OUTPUT INSERTED.invoiceNumber ;;
Perhatikan bahwa:
- Anda harus jalankan ini dalam transaksi berseri
- Transaksi harus sama dengan yang dimasukkan ke tabel tujuan (faktur).
Itu benar, Anda masih akan mendapatkan pemblokiran per bisnis saat mengeluarkan nomor faktur. Anda tidak bisa hindari ini jika nomor faktur harus berurutan, tanpa celah - sampai baris benar-benar dikomit, mungkin akan dibatalkan, artinya nomor faktur tidak akan diterbitkan.
Sekarang, karena Anda tidak ingin mengingat untuk memanggil prosedur entri, bungkus dalam pemicu:
CREATE TRIGGER Populate_Invoice_Number ON Invoice INSTEAD OF INSERT
AS
DECLARE @invoiceNumber INTEGER
BEGIN
EXEC Next_Invoice_Number Inserted.base, @invoiceNumber OUTPUT
INSERT INTO Invoice (base, invoiceNumber)
VALUES (Inserted.base, @invoiceNumber)
END
(jelas, Anda memiliki lebih banyak kolom, termasuk kolom lain yang harus diisi secara otomatis - Anda harus mengisinya)
...yang kemudian dapat Anda gunakan hanya dengan mengatakan:
INSERT INTO Invoice (base) VALUES('A');
Jadi apa yang telah kita lakukan? Sebagian besar, semua pekerjaan ini adalah tentang menyusutkan jumlah baris yang dikunci oleh suatu transaksi. Sampai INSERT
di-commit, hanya ada dua baris yang terkunci:
- Baris di
Invoice_Sequence
mempertahankan nomor urut - Baris di
Invoice
untuk faktur baru.
Semua baris lain untuk base
tertentu gratis - mereka dapat diperbarui atau ditanyakan sesuka hati (menghapus informasi dari sistem semacam ini cenderung membuat akuntan gugup). Anda mungkin perlu memutuskan apa yang akan terjadi ketika kueri biasanya menyertakan faktur yang menunggu keputusan...