web-dev-qa-db-fra.com

Comment obtenir la prochaine valeur d'identité à partir de SQL Server

J'ai besoin d'obtenir la prochaine valeur d'identité de SQL Server.

J'utilise ce code:

SELECT IDENT_CURRENT('table_name') + 1

C'est correct, mais lorsque le table_name est vide (et la prochaine valeur d'identité est "1") renvoie "2" mais le résultat est "1"

23
Mehdi Esmaeili

Je pense que vous voudrez chercher une autre façon de calculer la prochaine valeur disponible (telle que définir la colonne sur incrémentation automatique ).

De la documentation IDENT_CURRENT , concernant les tables vides:

Lorsque la valeur IDENT_CURRENT est NULL (car la table n'a jamais contenu de lignes ou a été tronquée), la fonction IDENT_CURRENT renvoie la valeur de départ.

Cela ne semble même pas très fiable, surtout si vous finissez par concevoir une application avec plus d'une personne écrivant sur la table en même temps.

Soyez prudent lorsque vous utilisez IDENT_CURRENT pour prédire la prochaine valeur d'identité générée. La valeur générée réelle peut être différente de IDENT_CURRENT plus IDENT_INCR en raison des insertions effectuées par d'autres sessions.

11
Grant

Dans le cas où votre table sera vide, cette requête fonctionnera parfaitement.

SELECT
  CASE
    WHEN (SELECT
        COUNT(1)
      FROM tablename) = 0 THEN 1
    ELSE IDENT_CURRENT('tablename') + 1
  END AS Current_Identity;
6
Abhijit Gosavi

J'ai tendance à être d'accord avec d'autres affiches sur le fait que ce n'est pas la bonne façon de procéder, mais cela peut être pratique dans certains cas. Plusieurs articles demandent pourquoi faire cela et laissez-moi vous donner un exemple où cela m'a été pratique, comment et pourquoi.

J'implémente un nœud Bitcoin. Je veux que la blockchain soit stockée dans une base de données SQL. Chaque bloc est reçu du réseau des autres nœuds et mineurs. Les détails que vous pouvez trouver ailleurs.

Lors de la réception d'un bloc, il contient un en-tête, un nombre quelconque de transactions et chaque transaction un nombre quelconque d'entrées et de sorties. J'ai 4 tables dans ma base de données - vous l'avez deviné - une table d'en-tête, une table de transactions, une table d'entrées et une table de sorties. Chaque ligne de la table des transactions, des entrées et des sorties est liée avec des ID les uns aux autres dans la ligne d'en-tête.

Certains blocs contiennent plusieurs milliers de transactions. Certaines transactions représentent des centaines d'entrées et/ou de sorties. J'ai besoin qu'ils soient stockés dans la base de données à partir d'un appel pratique en C # sans compromettre l'intégrité (les ID sont tous liés) et avec des performances décentes - ce que je ne peux pas obtenir en validant ligne par ligne lorsqu'il y a près de 10000 validations.

Au lieu de cela, je m'assure absolument de verrouiller la synchronisation de mon objet de base de données en C # pendant l'opération (et je n'ai pas à me soucier des autres processus accédant également à la base de données), donc je peux facilement faire un IDENT_CURRENT sur les 4 tables, renvoyer le valeurs d'un proc stocké, remplissez les près de 10000 lignes dans 4 List <DBTableRow> tout en incrémentant les ID et appelez la méthode SqlBulkCopy.WriteToServer avec l'option SqlBulkCopyOptions.KeepIdentity set, puis envoyez-les toutes en 4 appels simples, un pour chaque ensemble de tables .

Le gain de performances (sur un ordinateur portable de milieu de gamme âgé de 4 à 5 ans) allait d'environ 60 à 90 secondes à 2 à 3 secondes pour les très gros blocs, donc j'étais heureux d'en apprendre plus sur IDENT_CURRENT ().

La solution n'est peut-être pas élégante, peut-être pas pour ainsi dire, mais elle est pratique et simple. Il existe également d'autres moyens d'y parvenir, je le sais, mais cela a été simple et a pris quelques heures à mettre en œuvre. Assurez-vous simplement que vous n'avez pas de problèmes de concurrence.

5
Søren L. Fog

Je sais qu'il y a déjà une réponse, mais cela m'énerve vraiment que toutes mes recherches dans le sens de "obtenir le prochain serveur sql d'identité" aient abouti à des solutions floconneuses (comme simplement sélectionner la valeur d'identité actuelle et ajouter 1) ou "ça peut" t être fait de manière fiable ".

Il y a deux façons de le faire.

SQL Server> = 2012

CREATE SEQUENCE dbo.seq_FooId START WITH 1 INCREMENT BY 1
GO

CREATE TABLE dbo.Foos (
    FooId int NOT NULL 
        DEFAULT (NEXT VALUE FOR dbo.seq_FooId)
        PRIMARY KEY CLUSTERED 
)
GO

// Get the next identity before an insert
DECLARE @next_id = NEXT VALUE FOR dbo.seq_FooId

SQL Server 2012 a introduit l'objet SEQUENCE. Dans ce cas, la séquence sera incrémentée à chaque appel de NEXT VALUE FOR, Vous n'avez donc pas à vous soucier de la simultanéité.

SQL Server <= 2008

CREATE TABLE dbo.Foos (
    FooId int NOT NULL 
        IDENTITY (1, 1)
        PRIMARY KEY CLUSTERED 
)
GO

// Get the next identity before an insert
BEGIN TRANSACTION
SELECT TOP 1 1 FROM dbo.Foos WITH (TABLOCKX, HOLDLOCK)
DECLARE @next_id int = IDENT_CURRENT('dbo.Foos') + IDENT_INCR('dbo.Foos');
DBCC CHECKIDENT('dbo.Foos', RESEED, @next_id)
COMMIT TRANSACTION

Vous souhaiterez probablement encapsuler tout cela dans une procédure stockée, en particulier parce que l'instruction DBCC nécessite un accès élevé et vous ne voudrez probablement pas que tout le monde ait ce type d'accès.

Pas aussi élégant que NEXT VALUE FOR, Mais il devrait être fiable. Notez que vous obtiendrez 2 Pour votre première valeur s'il n'y a pas de lignes dans la table, mais si vous avez l'intention de toujours utiliser cette méthode pour obtenir la prochaine identité, vous pouvez amorcer l'identité à 0 au lieu de 1 (avec IDENTITY (0, 1)) si vous êtes prêt à démarrer avec 1.

Pourquoi quelqu'un voudrait-il faire ça?

Je ne peux pas parler pour l'affiche de la question, mais le livre 'Domain Driven Design' et l'exemple DDD 'officiel' utilise cette technique (ou du moins l'indique) comme un moyen de faire en sorte que les entités aient toujours un identifiant valide. Si votre entité a un faux identificateur (comme -1 Ou default(int) ou null) jusqu'à ce qu'il soit INSERTed dans la base de données, il est potentiellement une fuite de persistance préoccupation.

3
tuespetre
SELECT isnull(IDENT_CURRENT('emp') + IDENT_INCR('emp'),1)
2
Islam Gx