Comment changer les valeurs de colonne d'identité par programme?

160

J'ai une base de données MS SQL 2005 avec une table Testavec colonne ID. IDest une colonne d'identité.

J'ai des lignes dans ce tableau et toutes ont leur valeur incrémentée automatique ID correspondante.

Maintenant, je voudrais changer chaque identifiant de ce tableau comme ceci:

ID = ID + 1

Mais quand je fais cela, j'obtiens une erreur:

Impossible de mettre à jour la colonne d'identité "ID".

J'ai essayé ceci:

    ALTER TABLE Test NOCHECK CONSTRAINT ALL 
    set identity_insert ID ON

Mais cela ne résout pas le problème.

J'ai besoin d'avoir une identité définie sur cette colonne, mais je dois également modifier les valeurs de temps en temps. Ma question est donc de savoir comment accomplir cette tâche.

Tomasz Smykowski
la source

Réponses:

220

Tu dois

set identity_insert YourTable ON

Supprimez ensuite votre ligne et réinsérez-la avec une identité différente.

Une fois que vous avez terminé l'insertion, n'oubliez pas de désactiver identity_insert

set identity_insert YourTable OFF
AK
la source
133
le réglage ne fonctionnera que lors de l'insertion de données et non lors de la mise à jour. L'instruction UPDATE échouera toujours.
Husein Roncevic
31
Pour une mise à jour, vous devez supprimer et réinsérer. Il n'y a pas d'autre chemin.
ashes999
1
@MartinSmith votre solution semble trop longue. Être capable de le faire en deux étapes (désactivation de l'identité, suppression / insertion, activation de l'identité) est beaucoup plus efficace.
ashes999
3
@ ashes999 Cela fait 4 étapes et non 2. Ma réponse n'est qu'une de plus (créer une table, changer, mettre à jour, revenir en arrière, supprimer). Il est probable que vous êtes plus familier avec ces étapes, donc elles semblent moins compliquées. La mise à jour peut être beaucoup plus efficace que la suppression de l'insertion lorsque de nombreuses lignes sont impliquées. Où tenez-vous également les lignes supprimées? Si une #temptable que vous supprimez ensuite, c'est une autre étape que vous n'avez pas comptée ici.
Martin Smith
40

IDENTITY les valeurs de colonne sont immuables.

Cependant, il est possible de changer les métadonnées de la table pour supprimer la IDENTITYpropriété, faire la mise à jour, puis revenir en arrière.

En supposant la structure suivante

CREATE TABLE Test
(
ID INT IDENTITY(1,1) PRIMARY KEY,
X VARCHAR(10)
)

INSERT INTO Test 
OUTPUT INSERTED.*
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'

Alors tu peux faire

/*Define table with same structure but no IDENTITY*/
CREATE TABLE Temp
(
ID INT PRIMARY KEY,
X VARCHAR(10)
)

/*Switch table metadata to new structure*/
ALTER TABLE Test SWITCH TO Temp;

/*Do the update*/
UPDATE Temp SET ID = ID + 1;

/*Switch table metadata back*/
ALTER TABLE Temp SWITCH TO Test;

/*ID values have been updated*/
SELECT *
FROM Test

/*Safety check in case error in preceding step*/
IF NOT EXISTS(SELECT * FROM Temp)
    DROP TABLE Temp /*Drop obsolete table*/

Dans SQL Server 2012, il est possible d'avoir une colonne à incrémentation automatique qui peut également être mise à jour plus simplement avec SEQUENCES

CREATE SEQUENCE Seq
    AS INT
    START WITH 1
    INCREMENT BY 1

CREATE TABLE Test2
(
ID INT DEFAULT NEXT VALUE FOR Seq NOT NULL PRIMARY KEY,
X VARCHAR(10)
)

INSERT INTO Test2(X)
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'

UPDATE Test2 SET ID+=1
Martin Smith
la source
1
Très lisse. Apprenez quelque chose de nouveau chaque jour (BTW, j'ai testé cela sur SQLExpress; malgré le SWITCH TO, il n'utilise pas de partitionnement, apparemment). Notez que la table doit correspondre à peu près exactement (index, FK, etc.)
Mark Sowul
1
La méthode de commutation fonctionne-t-elle lorsque PK, FK, index et vues sont basés sur la table d'origine? Vont-ils casser en utilisant cette méthode?
tj
1
@tj lors de la création du script de table temporaire sur la table source, y compris tous les index et contraintes. Modifiez ensuite les noms de la table et des contraintes pour éviter les collisions. Les vues ne poseront aucun problème à moins qu'elles ne soient indexées ou associées à un schéma.
Martin Smith
Ne fonctionne pas s'il existe un index de recherche en texte intégral sur la table. Je suppose qu'il est possible de le supprimer et de le recréer par la suite, mais je n'ai pas testé.
Youen
26

Via l'interface utilisateur dans le gestionnaire SQL Server 2005, modifiez la colonne, supprimez la propriété de numéro automatique (identité) de la colonne (sélectionnez la table en cliquant dessus avec le bouton droit de la souris et choisissez «Conception»).

Exécutez ensuite votre requête:

UPDATE table SET Id = Id + 1

Ensuite, ajoutez la propriété de numérotation automatique à la colonne.

Michael Pryor
la source
3
Si vous effectuez la modification manuellement, vous pouvez demander au gestionnaire de générer le script SQL de la modification (menu du concepteur de table, générer un script de modification). Pour cette modification, il crée une nouvelle table et copie les données, puis supprime l'original.
Robin Bennett
2
@tomaszs - Un exemple de code qui est également plus efficace car il ne reconstruit pas physiquement la table du tout (cela le ferait deux fois) est dans ma réponse tardive ici
Martin Smith
Vous ne pouvez pas rajouter l'identité. Le faites vous? stackoverflow.com/questions/1049210/…
Tomas Kubes
Merci. C'était parfait. J'avais 1 ligne dans ma table dont je n'étais pas au courant, donc mes scripts d'amorçage pour ma table avaient des Idvaleurs d'identité automatique désactivées de 1.
Shiva
18

Premièrement, l'activation ou la désactivation de IDENTITY_INSERT ne fonctionnera pas pour ce dont vous avez besoin (il est utilisé pour insérer de nouvelles valeurs, telles que le colmatage des espaces).

L'opération via l'interface graphique crée simplement une table temporaire, copie toutes les données dans une nouvelle table sans champ d'identité et renomme la table.

Miles D
la source
9

Cela peut être fait à l'aide d'une table temporaire.

L'idée

  • désactiver les contraintes (dans le cas où votre identifiant est référencé par une clé étrangère)
  • créer une table temporaire avec le nouvel identifiant
  • supprimer le contenu de la table
  • recopier les données de la table copiée vers votre table d'origine
  • activer les contraintes précédemment désactivées

Requêtes SQL

Supposons que votre testtable ait deux colonnes supplémentaires ( column2et column3) et qu'il y ait 2 tables ayant des clés étrangères référencées testappelées foreign_table1et foreign_table2(car les problèmes de la vie réelle ne sont jamais simples).

alter table test nocheck constraint all;
alter table foreign_table1 nocheck constraint all;
alter table foreign_table2 nocheck constraint all;
set identity_insert test on;

select id + 1 as id, column2, column3 into test_copy from test v;
delete from test;
insert into test(id, column2, column3)
select id, column2, column3 from test_copy

alter table test check constraint all;
alter table foreign_table1 check constraint all;
alter table foreign_table2 check constraint all;
set identity_insert test off;
drop table test_copy;

C'est tout.

Manitra Andriamitondra
la source
6

DBCC CHECKIDENT ('databasename.dbo.orders', RESEED, 999) vous pouvez modifier n'importe quel numéro de colonne d'identité avec cette commande, et vous pouvez également démarrer ce numéro de champ à partir de chaque numéro que vous voulez.Par exemple, dans ma commande, je demande de commencer à partir de 1000 (999 + 1) espèrent que ce serait suffisant ... bonne chance

Roxana
la source
4

Si la colonne n'est pas un PK, vous pouvez toujours créer une NOUVELLE colonne dans la table avec les nombres incrémentés, supprimer l'original, puis modifier la nouvelle pour qu'elle devienne l'ancienne.

curieux de savoir pourquoi vous pourriez avoir besoin de faire cela ... la plupart que j'ai jamais eu à futz avec des colonnes d'identité était de remplir des numéros et j'ai juste fini par utiliser DBCC CHECKIDENT (tablename, RESEED, newnextnumber)

bonne chance!

Christopher Klein
la source
1

La modification d'identité peut échouer en fonction d'un certain nombre de facteurs, principalement autour des objets / relations liés à la colonne id. Il semble que la conception de la base de données soit aussi problématique ici que les identifiants devraient rarement, voire jamais, changer (je suis sûr que vous avez vos raisons et que vous effectuez les changements). Si vous avez vraiment besoin de changer les identifiants de temps en temps, je vous suggère de créer une nouvelle colonne d'identifiant factice qui n'est pas la clé primaire / numéro automatique que vous pouvez gérer vous-même et générer à partir des valeurs actuelles. Alternativement, l'idée de Chrisotphers ci-dessus serait mon autre suggestion si vous rencontrez des problèmes pour autoriser l'insertion d'identité.

Bonne chance

PS, cela n'échoue pas parce que l'ordre séquentiel dans lequel il s'exécute tente de mettre à jour une valeur de la liste vers un élément qui existe déjà dans la liste des identifiants? en s'accrochant à des pailles, ajoutez peut-être le nombre de lignes + 1, puis si cela fonctionne, soustrayez le nombre de lignes: -S

Tanneur
la source
1

Si vous devez modifier les identifiants occasionnellement, il est probablement préférable de ne pas utiliser de colonne d'identité. Dans le passé, nous avons implémenté manuellement les champs de numérotation automatique à l'aide d'une table «Compteurs» qui suit l'ID suivant pour chaque table. Nous l'avons fait parce que les colonnes d'identité causaient une corruption de la base de données dans SQL2000, mais la possibilité de modifier les ID était parfois utile pour les tests.

Robin Bennett
la source
1

Vous pouvez insérer de nouvelles lignes avec des valeurs modifiées, puis supprimer les anciennes lignes. L'exemple suivant change l'ID pour qu'il soit identique à la clé étrangère PersonId

SET IDENTITY_INSERT [PersonApiLogin] ON

INSERT INTO [PersonApiLogin](
       [Id]
      ,[PersonId]
      ,[ApiId]
      ,[Hash]
      ,[Password]
      ,[SoftwareKey]
      ,[LoggedIn]
      ,[LastAccess])
SELECT [PersonId]
      ,[PersonId]
      ,[ApiId]
      ,[Hash]
      ,[Password]
      ,[SoftwareKey]
      ,[LoggedIn]
      ,[LastAccess]
FROM [db304].[dbo].[PersonApiLogin]
GO

DELETE FROM [PersonApiLogin]
WHERE [PersonId] <> ID
GO
SET IDENTITY_INSERT [PersonApiLogin] OFF
GO
Tomas Kubes
la source
1

Enregistrez d'abord tous les ID et modifiez-les par programme aux valeurs que vous ne souhaitez pas, puis supprimez-les de la base de données, puis insérez-les à nouveau en utilisant quelque chose de similaire:

use [Name.Database]
go
set identity_insert [Test] ON
insert into [dbo].[Test]
           ([Id])
     VALUES
           (2)
set identity_insert [Test] OFF

Pour une utilisation en vrac:

use [Name.Database]
go
set identity_insert [Test] ON
BULK INSERT [Test]
FROM 'C:\Users\Oscar\file.csv'
WITH (FIELDTERMINATOR = ';',
      ROWTERMINATOR = '\n',
      KEEPIDENTITY)
set identity_insert [Test] OFF

Exemple de données de file.csv:

2;
3;
4;
5;
6;

Si vous ne identity_insertdésactivez pas, vous obtiendrez l'erreur suivante:

Impossible d'insérer une valeur explicite pour la colonne d'identité dans la table 'Test' lorsque IDENTITY_INSERT est défini sur OFF.

Ogglas
la source
0

J'ai vu un bon article qui m'a aidé au dernier moment. J'essayais d'insérer quelques lignes dans un tableau qui avait une colonne d'identité mais je l'ai fait à tort et je dois supprimer. Une fois que j'ai supprimé les lignes, ma colonne d'identité a été modifiée. J'essayais de trouver un moyen de mettre à jour la colonne qui a été insérée mais - pas de chance. Ainsi, lors de la recherche sur Google, un lien a été trouvé.

  1. Suppression des colonnes mal insérées
  2. Utiliser l'insertion forcée en activant / désactivant l'identité (expliqué ci-dessous)

http://beyondrelational.com/modules/2/blogs/28/posts/10337/sql-server-how-do-i-insert-an-explicit-value-into-an-identity-column-how-do- i-update-the-value-of-an.aspx

Balu
la source
1
Je ne suis pas sûr que vous ayez vraiment répondu à la question ici.
Andrew Barber
0

Très belle question, nous devons d'abord sur IDENTITY_INSERT pour la table spécifique, après cela exécutez la requête d'insertion (Doit spécifier le nom de la colonne).

Remarque: après avoir modifié la colonne d'identité, n'oubliez pas de désactiver IDENTITY_INSERT. Si vous ne l'avez pas fait, vous ne pourrez pas modifier la colonne d'identité pour une autre table.

SET IDENTITY_INSERT Emp_tb_gb_Menu ON
     INSERT Emp_tb_gb_Menu(MenuID) VALUES (68)
SET IDENTITY_INSERT Emp_tb_gb_Menu OFF

http://allinworld99.blogspot.com/2016/07/how-to-edit-identity-field-in-sql.html

Asith Raj
la source