web-dev-qa-db-fra.com

Lacunes inattendues dans la colonne IDENTITY

J'essaie de générer des numéros de bons de commande uniques qui commencent à 1 et augmentent par 1. J'ai une table PONumber créée à l'aide de ce script:

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

Et une procédure stockée créée à l'aide de ce script:

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

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

Au moment de la création, cela fonctionne bien. Lorsque la procédure stockée s'exécute, elle démarre au nombre souhaité et incrémente de 1.

Ce qui est étrange, c'est que si j'arrête ou met en veille prolongée mon ordinateur, la prochaine fois que la procédure s'exécute, la séquence avance de près de 1000.

Voir les résultats ci-dessous:

PO Numbers

Vous pouvez voir que le nombre est passé de 8 à 1002!

  • Pourquoi cela arrive-t-il?
  • Comment puis-je m'assurer que les numéros ne sont pas sautés comme ça?
  • Tout ce dont j'ai besoin, c'est que SQL génère des nombres qui sont:
    • a) Garantie unique.
    • b) incrémenter du montant souhaité.

J'avoue que je ne suis pas un expert SQL. Dois-je mal comprendre ce que fait SCOPE_IDENTITY ()? Dois-je utiliser une approche différente? J'ai examiné les séquences dans SQL 2012+, mais Microsoft dit qu'elles ne sont pas garanties d'être uniques par défaut.

18
Ege Ersoz

Il s'agit d'un problème connu et attendu - la façon dont les colonnes IDENTITY sont gérées par SQL Server a changé dans SQL Server 2012 ( certains antécédents ); par défaut, il mettra en cache 1000 valeurs et si vous redémarrez SQL Server, redémarrez le serveur, basculez, etc. il devra jeter ces 1000 valeurs, car il n'aura pas de moyen fiable de savoir combien d'entre elles étaient réellement Publié. Ceci est documenté ici . Il existe un indicateur de trace qui modifie ce comportement de telle sorte que chaque affectation IDENTITY est enregistrée *, empêchant ces lacunes spécifiques (mais pas les lacunes des annulations ou des suppressions); cependant, il est important de noter que cela peut être assez coûteux en termes de performances, donc je ne vais même pas mentionner ici l'indicateur de trace spécifique.

* (Personnellement, je pense que c'est un problème technique qui pourrait être résolu différemment, mais comme je n'écris pas le moteur, je ne peux pas le changer.)

Pour être clair sur le fonctionnement de l'IDENTITÉ et de la SÉQUENCE:

  • Aucun des deux n'est garanti comme étant unique (vous devez appliquer cela au niveau de la table, en utilisant une clé primaire ou une contrainte unique)
  • Aucun des deux n'est garanti sans faille (toute annulation ou suppression, par exemple, produira un écart, malgré ce problème spécifique)

L'unicité est facile à appliquer. Éviter les lacunes ne l'est pas. Vous devez déterminer à quel point il est important pour vous d'éviter ces lacunes (en théorie, vous ne devriez pas vous soucier des lacunes, car les valeurs IDENTITY/SEQUENCE doivent être des clés de substitution sans signification). Si c'est très important, alors vous ne devriez pas utiliser l'une ou l'autre implémentation, mais plutôt lancer votre propre générateur de séquence sérialisable (voir quelques idées ici , ici et ici ) - notez simplement que cela tuera la concurrence.

Beaucoup d'histoire sur ce "problème":

25
Aaron Bertrand