Aggiungi l'incremento automatico al PK esistente


14

Ho creato una tabella in un DB che esiste già in un altro DB. Inizialmente era popolato con i vecchi dati DB. Il PK della tabella doveva ricevere i valori già esistenti su quei record, quindi non poteva essere un incremento automatico.

Ora ho bisogno che il nuovo tavolo abbia il suo PK come incremento automatico. Ma come posso farlo dopo che il PK esiste già e dispone di dati?


3
Quando dici "autoincremento" a cosa ti riferisci esattamente ? In SQL Server non esiste tale proprietà per una colonna. Intendi IDENTITY?
Max Vernon,

Sì, è così che si chiama in MSSQL. Nel database in generale, è un PK di autoincremento.
Hikari,

Risposte:


14

Il modo in cui capisco la tua domanda è che hai una tabella esistente con una colonna che finora è stata popolata con valori manuali, e ora vuoi (1) rendere questa colonna una IDENTITYcolonna e (2) assicurarti che gli IDENTITYinizi dal valore più recente nelle righe esistenti.

Prima di tutto, alcuni dati di test con cui giocare:

CREATE TABLE dbo.ident_test (
    id    int NOT NULL,
    xyz   varchar(10) NOT NULL,
    CONSTRAINT PK_ident_test PRIMARY KEY CLUSTERED (id)
);

INSERT INTO dbo.ident_test (id, xyz)
VALUES (1, 'test'),
       (2, 'test'),
       (5, 'test'),
       (6, 'test'),
       (10, 'test'),
       (18, 'test'),
       (19, 'test'),
       (20, 'test');

L'obiettivo è creare la colonna chiave primaria della tabella id, una IDENTITYcolonna che inizierà alle 21 per il record successivo che verrà inserito. Per questo esempio, la colonna xyzrappresenta tutte le altre colonne della tabella.

Prima di fare qualsiasi cosa, leggi gli avvisi in fondo a questo post.

Prima di tutto, nel caso in cui qualcosa vada storto:

BEGIN TRANSACTION;

Ora, aggiungiamo una colonna di lavoro temporanea id_tempe impostiamo quella idcolonna sui valori della colonna esistente :

ALTER TABLE dbo.ident_test ADD id_temp int NULL;
UPDATE dbo.ident_test SET id_temp=id;

Successivamente, dobbiamo eliminare la idcolonna esistente (non puoi semplicemente "aggiungere" una IDENTITYcolonna esistente, devi creare la colonna come IDENTITY). Anche la chiave primaria deve andare, perché la colonna dipende da essa.

ALTER TABLE dbo.ident_test DROP CONSTRAINT PK_ident_test;
ALTER TABLE dbo.ident_test DROP COLUMN id;

... e aggiungi nuovamente la colonna, questa volta come IDENTITY, insieme alla chiave primaria:

ALTER TABLE dbo.ident_test ADD id int IDENTITY(1, 1) NOT NULL;
ALTER TABLE dbo.ident_test ADD CONSTRAINT PK_ident_test PRIMARY KEY CLUSTERED (id);

Ecco dove diventa interessante. È possibile abilitare IDENTITY_INSERTsulla tabella, il che significa che è possibile definire manualmente i valori di una IDENTITYcolonna quando si inseriscono nuove righe (non aggiornando le righe esistenti, però).

SET IDENTITY_INSERT dbo.ident_test ON;

Con quel set, DELETEtutte le righe nella tabella, ma le righe che stai eliminando si trovano OUTPUTnella stessa tabella, ma con valori specifici per la idcolonna (dalla colonna di backup).

DELETE FROM dbo.ident_test
OUTPUT deleted.id_temp AS id, deleted.xyz
INTO dbo.ident_test (id, xyz);

Una volta fatto, IDENTITY_INSERTspegnere di nuovo.

SET IDENTITY_INSERT dbo.ident_test OFF;

Rilascia la colonna temporanea che abbiamo aggiunto:

ALTER TABLE dbo.ident_test DROP COLUMN id_temp;

E infine, ridimensionato la IDENTITYcolonna, quindi i record successivi idriprenderanno dopo il numero più alto esistente nella idcolonna:

DECLARE @maxid int;
SELECT @maxid=MAX(id) FROM dbo.ident_test;
DBCC CHECKIDENT ("dbo.ident_test", RESEED, @maxid)

Controllando la tabella di esempio, il idnumero più alto è 20.

SELECT * FROM dbo.ident_test;

Aggiungi un'altra riga e controlla la sua nuova IDENTITY:

INSERT INTO dbo.ident_test (xyz) VALUES ('New row');
SELECT * FROM dbo.ident_test;

Nell'esempio, la nuova riga avrà id=21. Infine, se sei soddisfatto, esegui la transazione:

COMMIT TRANSACTION;

Importante

Questa non è un'operazione banale e comporta alcuni rischi di cui dovresti essere consapevole.

  • Fallo in un ambiente di test dedicato. Avere backup. :)

  • Mi piace usare BEGIN/COMMIT TRANSACTIONperché impedisce ad altri processi di scherzare con il tavolo mentre sei nel mezzo di cambiarlo, e ti dà la possibilità di ripristinare tutto se qualcosa va storto. Tuttavia, qualsiasi altro processo che tenta di accedere al tuo tavolo prima di aver eseguito il commit della transazione finirà per attendere. Questo può essere piuttosto negativo se si dispone di una tabella di grandi dimensioni e / o ci si trova in un ambiente di produzione.

  • OUTPUT .. INTOnon funzionerà se la tabella di destinazione presenta vincoli di chiave esterna o una qualsiasi delle altre funzionalità che non ricordo dalla parte superiore della mia testa. È possibile invece scaricare i dati in una tabella temporanea e quindi reinserirli nella tabella originale. Potresti essere in grado di utilizzare il cambio di partizione (anche se non usi le partizioni).

  • Esegui queste istruzioni una per una, non come un batch o in una stored procedure.

  • Prova a pensare ad altre cose che potrebbero dipendere dalla idcolonna che stai abbandonando e ricreando. Eventuali indici dovranno essere eliminati e ricreati (come abbiamo fatto con la chiave primaria). Ricorda di scrivere ogni indice e vincolo che dovrai ricreare in anticipo.

  • Disabilita qualsiasi INSERTe DELETEtrigger sul tavolo.

Se ricreare la tabella è un'opzione:

Se ricreare la tabella è un'opzione per te, tutto è molto più semplice:

  • Crea la tabella vuota, con la idcolonna come IDENTITY,
  • Apparecchiato IDENTITY_INSERT ONper il tavolo,
  • Popolare il tavolo,
  • Set IDENTITY_INSERT OFFe
  • Ridistribuito l'identità.

Ottima risposta, grazie mille! In effetti, nel mio caso, posso semplicemente impostarlo IDENTITY_INSERT ON, popolarlo e disabilitarlo. Questo è quello che volevo fare, ma non sapevo che MSSQL lo supportasse.
Hikari,

5

L'uso di UPDATE, DELETE o INSERT per spostare i dati può richiedere parecchio tempo e utilizzare le risorse (IO) su entrambi i dati e i file / dischi di registro. È possibile evitare di riempire il registro delle transazioni con potenzialmente molti record mentre si lavora su una tabella di grandi dimensioni: utilizzando il cambio di partizione, vengono modificati solo i metadati.

Non è richiesto alcun movimento di dati e questo viene quindi eseguito molto rapidamente (quasi istantaneamente).

Tabella di esempio

La domanda non mostra la tabella DDL originale. Il seguente DDL verrà utilizzato come esempio in questa risposta:

CREATE TABLE dbo.idT(
    id int not null
    , uid uniqueidentifier not null
    , name varchar(50)
);
ALTER TABLE dbo.idT ADD CONSTRAINT PK_idT PRIMARY KEY CLUSTERED(id);

Con questa query vengono aggiunte mezza dozzina di ID casuali fittizi da 0 a 15:

WITH ids(n) AS(
    SELECT x1.n+x2.n*4
    FROM (values(0), (3)) as x1(n)
    CROSS JOIN (values(0), (2), (3)) as x2(n)
)
INSERT INTO idt(id, uid, name)
SELECT n, NEWID(), NEWID() 
FROM ids

Dati di esempio in IdT

id  uid                                     name
0   65533096-5007-43EA-88AD-D6776B3B94FA    6A69D4F2-D682-4168-A92F-4CD2E2DBC21D
3   CE87F1ED-BE1A-4F2D-8D62-E1ECA822D35B    AF0524D9-0DBB-41E1-883B-003CB4E4F012
8   34A1DBFD-4F92-4F34-9F04-4CDC824AB15A    02B4BDA4-D515-4262-9031-0BE496AC24CE
11  51606C95-9DE8-4C30-B23B-F915EEA41156    93258103-9C22-4F9C-85CF-712ED0FB3CE6
12  CEC80431-0513-4751-A250-0EB3390DACAB    2DA6B8AF-3EBC-42B3-A76C-028716E24661
15  5037EA83-286F-4EBC-AD7C-E237B570C1FF    095E51E9-8C38-4104-858F-D14AA810A550

Nuovo tavolo con IDENTITY(0, 1)

L'unico problema idTè la mancanza della IDENTITY(0, 1)proprietà su ID. Viene IDENTITY(0, 1)creata una nuova tabella con una struttura simile :

CREATE TABLE dbo.idT_Switch(
    id int identity(0, 1) not null
    , uid uniqueidentifier not null
    , name varchar(50)
);
ALTER TABLE dbo.idT_Switch ADD CONSTRAINT PK_idT_Switch PRIMARY KEY CLUSTERED(id);

A parte IDENTITY(0, 1), idT_Switchè identico a idT.

Chiavi straniere

Le chiavi esterne su idTdevono essere rimosse per consentire l'utilizzo di questa tecnica.

Interruttore di partizione

Le tabelle idTe idT_Switchhanno una struttura compatibile. Invece di utilizzare DELETE, UPDATEe INSERTistruzioni per spostare righe da idTa idT_Switcho su idTse stessa, ALTER TABLE ... SWITCHpossono essere utilizzati:

ALTER TABLE dbo.idT
SWITCH TO dbo.idT_Switch;

La singola 'partizione' di PK_idT(l'intera tabella) viene spostata in PK_idT_Switch(e viceversa). idTora contiene 0 righe e idT_Switchcontiene 6 righe.

Puoi trovare l'elenco completo dei requisiti di compatibilità di origine e destinazione qui:

Trasferimento efficiente dei dati mediante la commutazione delle partizioni

Si noti che questo uso di SWITCHnon richiede Enterprise Edition, perché non esiste un partizionamento esplicito. Una tabella non partizionata è considerata una tabella con una singola partizione da SQL Server 2005 in poi.

Sostituire idT

idT è ora vuoto e inutile e può essere eliminato:

DROP TABLE idT;

idT_Switchpuò essere rinominato e sostituirà la vecchia idTtabella:

EXECUTE sys.sp_rename
    @objname = N'dbo.idT_Switch',
    @newname = N'idT', -- note lack of schema prefix
    @objtype = 'OBJECT';

Chiavi esterne

Le chiavi esterne possono essere aggiunte di nuovo alla nuova idTtabella. Qualunque altra cosa precedentemente rimossa idTper rendere compatibili le tabelle per la commutazione dovrà essere rifatta.

reseed

SELECT IDENT_CURRENT( 'dbo.idT');

Questo comando restituisce 0. La tabella idT contiene 6 righe con MAX (id) = 15. DBCC CHECKIDENT (table_name) può essere utilizzato:

DBCC CHECKIDENT ('dbo.idT');

Poiché 15 è maggiore di 0, verrà automaticamente ridimensionato senza cercare MAX (id):

Se il valore di identità corrente per una tabella è inferiore al valore di identità massimo memorizzato nella colonna identità, viene reimpostato utilizzando il valore massimo nella colonna identità. Vedi la sezione "Eccezioni" che segue.

IDENT_CURRENT ora restituisce 15 .

Testare e aggiungere dati

Una semplice INSERTdichiarazione:

INSERT INTO idT(uid, name) SELECT NEWID(), NEWID();

Aggiunge questa riga:

id  uid                                     name
16  B395D692-5D7B-4DFA-9971-A1497B8357A1    FF210D9E-4027-479C-B5D8-057E77FAF378

La idcolonna ora utilizza l'identità e il valore appena inserito è effettivamente 16 (15 + 1).

Maggiori informazioni

C'è una domanda e una risposta correlate con più informazioni sulla SWITCHtecnica qui:

Perché la rimozione della proprietà Identity su una colonna non è supportata


4

Se si desidera iniziare con un nuovo valore di identità, è necessario ridimensionare la propria identità. Dai un'occhiata alla documentazione diCHECKIDENT

DBCC CHECKIDENT (yourtable, reseed, starting point)

0

ENABLE e DISABLE IDENTITY_INSERT

Se il tuo tavolo è TABLE_A, allora

  1. CREA TABELLA TABELLA_B simile a TABELLA_A con la colonna identità
  2. SET IDENTITY_INSERT TABLE_B ON
  3. INSERISCI in TABLE_B da TABLE_A
  4. SET IDENTITY_INSERT TABLE_B OFF
  5. DROP TABLE TABLE_A e rinomina tabella B Exec sp_rename 'TABLE_B', 'TABLE_A'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.