Несподівані прогалини в стовпці "IDENTITY"


18

Я намагаюся генерувати унікальні номери замовлень на купівлю, які починаються з 1 і збільшуються на 1. У мене є таблиця PONumber, створена за допомогою цього сценарію:

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)    
);

І збережена процедура, створена за допомогою цього сценарію:

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

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

На момент створення це прекрасно працює. Коли збережена процедура запускається, вона починається з потрібного числа та з кроком на 1.

Дивна річ у тому, що якщо я вимкну або перезимував комп’ютер, то наступного разу, коли буде запущена процедура, послідовність просунулася майже на 1000.

Дивіться результати нижче:

PO номери

Видно, що число підскочило з 8 до 1002!

  • Чому це відбувається?
  • Як зробити так, щоб цифри не пропускалися так?
  • Все, що мені потрібно, це для SQL для генерації чисел, які є:
    • а) Гарантована унікальність.
    • б) приріст на потрібну суму.

Я визнаю, що я не експерт SQL. Чи я неправильно розумію, що робить SCOPE_IDENTITY ()? Чи варто використовувати інший підхід? Я роздивився послідовності в SQL 2012+, але Microsoft каже, що вони не гарантовано є унікальними за замовчуванням.

Відповіді:


25

Це відома і очікувана проблема - спосіб управління колонками IDENTITY за допомогою SQL Server змінився в SQL Server 2012 ( деякий фон ); за замовчуванням він буде кешувати 1000 значень, і якщо ви перезавантажите SQL Server, перезавантажте сервер, перестанете працювати і т.д. виданий. Це задокументовано тут . Існує прапор слідів, який змінює цю поведінку таким чином, що кожне призначення IDENTITY реєструється *, запобігаючи ці конкретні прогалини (але не прогалини від відкатів або видалення); однак, важливо зазначити, що це може бути досить дорогим з точки зору продуктивності, тому я навіть не збираюся згадувати конкретний прапор слідів.

* (Особисто я думаю, що це технічна проблема, яку можна вирішити інакше, але оскільки я не пишу двигун, я не можу це змінити.)

Щоб зрозуміти, як працюють ІДЕНТИЧНІСТЬ та ПОСЛІДНІСТЬ:

  • Жоден з них не гарантовано є унікальним (потрібно виконати це на рівні таблиці, використовуючи первинний ключ або унікальне обмеження)
  • Жоден з них не гарантується безперервним (будь-який відкат або видалення, наприклад, призведе до розриву, незважаючи на цю конкретну проблему)

Унікальність легко застосувати. Уникнення прогалин - ні. Вам потрібно визначити, наскільки важливо вам уникнути цих прогалин (теоретично, ви не повинні піклуватися про прогалини, оскільки значення IDENTITY / SEQUENCE мають бути безглуздими сурогатними ключами). Якщо це дуже важливо, тоді вам не слід використовувати жодну реалізацію, а скоріше скочувати власний генератор серіалізаційних послідовностей (див. Деякі ідеї тут , тут і тут ) - просто зауважте, що це вб'є одночасність.

Багато проблем щодо цієї "проблеми":


Ця відповідь (за винятком частини "прапор трасування") також стосується більшості інших баз даних SQL (тих, у яких все одно є послідовності).
mustaccio

Дякую за відповідь. Унікальність - це найважливіша вимога. Прогалини не є великою справою, якщо вони не великі. наприклад, перехід від 1 до 4 було б прийнятним, але від 4 до 1003 не було б.
Еге Ерсоз

1
Коротка версія: значення ідентифікаторів використовуватимуться як номери замовлення на покупку. Клієнт проводить щомісячні звіти і хоче, щоб він міг швидко повідомити, скільки ОП було подано в цьому місяці, просто переглянувши номер PO. Таким чином, ми не можемо збільшувати його на ~ 1000 (є щотижневе обслуговування, де перезапускаються всі сервери, включаючи сервер БД).
Еге Ерсоз

3
Чому ви не дасте їм дуже простий звіт, який просто використовує ROW_NUMBER () НАДЕЖ (РОЗДІЛ ЗА МІСЬКОМ ЗАМОВЛЕННЯМИ ІД)? Знову ж таки, ідентифікаційний номер повинен бути безглуздим, це жахливий спосіб оцінити кількість замовлень. Що робити, якщо у вашому коді є помилка, яка видаляє 1000 рядків або повертає 275 транзакцій, або 500 замовлень законно скасовуються?
Аарон Бертран

1
@Ege: "... скажи скільки ... просто подивившись номер PO". Ваші користувачі будуть розчаровані. Значення ідентичності просто не працюють таким чином, і ви не повинні (або вони) робити таке припущення. Унікальний? Так. Послідовно? Ні. Правильний спосіб підрахунку відправлених заявок протягом місяця - це ... підрахувати кількість поштових скриньок, піднятих за цей місяць, виходячи з деякого [незмінного] поля дати у кожному записі.
Phill W.

-4

Це проблема SQL Server. Все, що ви можете зробити, це перезавантажити стовпчик.

видаліть записи з неправильним ідентифікатором стовпця. Переглянуто особу стовпця. І тоді наступний запис має відповідний ідентифікатор.

Повторна перевірка ідентичності за допомогою наступної команди sql: DBCC CHECKIDENT ('YOUR_TABLE_NAME', RESEED, 9)- 9 - це останній правильний ідентифікатор


1
Що ви маєте на увазі під "видалити записи"?
ypercubeᵀᴹ

2
Гммм .. здається, видалення записів може призвести до втрати даних.
Майкл Грін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.