Kesenjangan yang tak terduga di kolom IDENTITAS

17

Saya mencoba untuk menghasilkan nomor pesanan pembelian unik yang dimulai dari 1 dan bertambah dengan 1. Saya memiliki tabel PONumber yang dibuat menggunakan skrip ini:

CREATE TABLE [dbo].[PONumbers]
(
  [PONumberPK] [int] IDENTITY(1,1) NOT NULL,
  [NewPONo] [bit] NOT NULL,
  [DateInserted] [datetime] NOT NULL DEFAULT GETDATE(),
  CONSTRAINT [PONumbersPK] PRIMARY KEY CLUSTERED ([PONumberPK] ASC)    
);

Dan prosedur tersimpan yang dibuat menggunakan skrip ini:

CREATE PROCEDURE [dbo].[GetPONumber] 
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [dbo].[PONumbers]([NewPONo]) VALUES(1);
    SELECT SCOPE_IDENTITY() AS PONumber;
END

Pada saat penciptaan, ini berfungsi dengan baik. Ketika prosedur tersimpan berjalan, itu dimulai pada nomor yang diinginkan dan bertambah 1.

Yang aneh adalah bahwa, jika saya mematikan atau hibernasi komputer saya, maka pada saat prosedur berjalan, urutannya telah meningkat hampir 1000.

Lihat hasil di bawah ini:

Nomor PO

Anda dapat melihat bahwa jumlahnya melonjak dari 8 menjadi 1002!

  • Mengapa ini terjadi?
  • Bagaimana cara memastikan bahwa angka tidak dilewati seperti itu?
  • Yang saya butuhkan adalah SQL untuk menghasilkan angka-angka yaitu:
    • a) Dijamin unik.
    • b) kenaikan dengan jumlah yang diinginkan.

Saya akui saya bukan ahli SQL. Apakah saya salah mengerti apa yang dilakukan SCOPE_IDENTITY ()? Haruskah saya menggunakan pendekatan yang berbeda? Saya melihat ke dalam urutan dalam SQL 2012+, tetapi Microsoft mengatakan bahwa mereka tidak dijamin unik secara default.

Ege Ersoz
sumber

Jawaban:

24

Ini adalah masalah yang diketahui dan diharapkan - cara kolom IDENTITY dikelola oleh SQL Server telah berubah di SQL Server 2012 ( beberapa latar belakang ); secara default akan men-cache 1000 nilai dan jika Anda me-restart SQL Server, me-reboot server, gagal, dll. itu harus membuang nilai-nilai 1000, karena itu tidak akan memiliki cara yang dapat diandalkan untuk mengetahui berapa banyak dari mereka yang sebenarnya dikabarkan. Ini didokumentasikan di sini . Ada tanda jejak yang mengubah perilaku ini sehingga setiap tugas IDENTITY dicatat *, mencegah celah-celah khusus tersebut (tetapi bukan celah dari pengembalian atau penghapusan); Namun, penting untuk dicatat bahwa ini bisa sangat mahal dalam hal kinerja, jadi saya bahkan tidak akan menyebutkan tanda jejak khusus di sini.

* (Secara pribadi, saya pikir ini adalah masalah teknis yang bisa diselesaikan secara berbeda, tetapi karena saya tidak menulis mesinnya, saya tidak bisa mengubahnya.)

Agar lebih jelas tentang cara kerja IDENTITAS dan URUTAN:

  • Tidak ada yang dijamin unik (Anda harus memaksakannya di tingkat tabel, menggunakan kunci utama atau batasan unik)
  • Tidak ada yang dijamin tanpa celah (setiap kemunduran atau penghapusan, misalnya, akan menghasilkan celah, meskipun masalah khusus ini)

Keunikan mudah ditegakkan. Menghindari kesenjangan tidak. Anda perlu menentukan seberapa penting bagi Anda untuk menghindari kesenjangan ini (secara teori, Anda seharusnya tidak peduli dengan kesenjangan sama sekali, karena nilai IDENTITAS / URUTAN harus menjadi kunci pengganti yang tidak berarti). Jika ini sangat penting, maka Anda tidak boleh menggunakan salah satu implementasi, tetapi lebih baik roll generator urutan serializable Anda sendiri (lihat beberapa ide di sini , di sini dan di sini ) - hanya perlu dicatat bahwa itu akan membunuh konkurensi.

Banyak latar belakang tentang "masalah" ini:

Aaron Bertrand
sumber
Jawaban ini (kecuali bagian "jejak bendera") juga berlaku untuk sebagian besar basis data SQL lainnya (yang tetap memiliki urutan).
mustaccio
Terima kasih atas jawabannya. Keunikan adalah satu-satunya persyaratan terpenting. Kesenjangan bukan masalah besar, asalkan tidak besar. misalnya pergi dari 1 ke 4 akan diterima, tetapi dari 4 ke 1003 tidak akan.
Ege Ersoz
1
Versi singkat: nilai ID akan digunakan sebagai nomor pesanan pembelian. Pelanggan menjalankan laporan bulanan dan ingin dapat dengan cepat memberi tahu berapa banyak PO yang dikirim bulan itu hanya dengan melihat nomor PO. Jadi kita tidak bisa menambahkannya hingga ~ 1000 (ada pemeliharaan mingguan di mana semua server, termasuk server DB, dimulai kembali).
Ege Ersoz
3
Mengapa Anda tidak memberi mereka laporan yang sangat mudah yang hanya menggunakan ROW_NUMBER () LEBIH (PARTISI DENGAN ORDER BULAN DENGAN ID)? Sekali lagi, nomor ID harus tidak berarti, itu adalah cara yang mengerikan untuk melihat berapa banyak pesanan yang diambil. Bagaimana jika Anda memiliki bug dalam kode Anda yang menghapus 1.000 baris atau memutar kembali 275 transaksi, atau 500 pesanan dibatalkan secara sah?
Aaron Bertrand
1
@Ege: "... beri tahu berapa ... hanya dengan melihat nomor PO". Pengguna Anda akan kecewa. Nilai-nilai identitas tidak berfungsi seperti itu, dan Anda (atau mereka) tidak boleh membuat asumsi semacam itu. Unik? Iya. Berurutan? Tidak. Cara yang benar untuk menghitung PO yang dikirim selama satu bulan adalah ... untuk menghitung jumlah PO yang diajukan selama bulan itu, berdasarkan pada beberapa bidang Tanggal [yang tidak dapat diubah] di setiap record.
Phill W.
-4

Ini adalah masalah SQL Server. Yang bisa Anda lakukan adalah memasang kembali kolom.

hapus entri dengan id kolom yang salah. Mempelajari kembali identitas kolom. Dan kemudian entri berikutnya memiliki ID yang tepat untuk itu.

Identifikasi ulang menggunakan perintah sql berikut: DBCC CHECKIDENT ('YOUR_TABLE_NAME', RESEED, 9)- 9 adalah Id yang benar terakhir

pengguna190684
sumber
1
Apa yang Anda maksud dengan "hapus entri"?
ypercubeᵀᴹ
2
Hmmm .. sepertinya menghapus entri mungkin saja menyebabkan hilangnya data.
Michael Green