Tambahkan peningkatan otomatis ke PK yang ada

13

Saya membuat tabel dalam DB yang sudah ada di DB lain. Awalnya diisi dengan data DB lama. PK tabel harus menerima nilai-nilai yang sudah ada pada catatan itu, jadi itu tidak bisa autoincrement.

Sekarang saya perlu tabel baru untuk memiliki PK sebagai autoincrement. Tetapi bagaimana saya bisa melakukan itu setelah PK sudah ada dan memiliki data?

Hikari
sumber
3
Ketika Anda mengatakan "peningkatan otomatis" apa sebenarnya yang Anda maksud? Di SQL Server tidak ada properti seperti itu untuk kolom. Apakah yang Anda maksud IDENTITY?
Max Vernon
Ya, begitulah disebut dalam MSSQL. Dalam basis data secara umum, ini adalah PK peningkatan otomatis.
Hikari

Jawaban:

13

Cara saya memahami pertanyaan Anda adalah bahwa Anda memiliki tabel yang sudah ada dengan kolom yang sampai sekarang telah diisi dengan nilai-nilai manual, dan sekarang Anda ingin (1) membuat kolom ini menjadi IDENTITYkolom, dan (2) memastikan bahwa IDENTITYpermulaan dari nilai terbaru di baris yang ada.

Pertama, beberapa data uji untuk dimainkan:

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

Tujuannya adalah untuk membuat kolom kunci utama tabel id,, sebuah IDENTITYkolom yang akan dimulai pada 21 untuk catatan berikutnya yang dimasukkan. Untuk contoh ini, kolom xyzmewakili semua kolom tabel lainnya.

Sebelum Anda melakukan apa pun, harap baca peringatan di bagian bawah pos ini.

Pertama, jika terjadi kesalahan:

BEGIN TRANSACTION;

Sekarang, mari kita tambahkan kolom kerja sementara, id_tempdan atur kolom itu ke nilai idkolom yang ada :

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

Selanjutnya, kita perlu menjatuhkan idkolom yang ada (Anda tidak bisa hanya "menambahkan" IDENTITYke kolom yang ada, Anda harus membuat kolom sebagai IDENTITY). Kunci utama juga harus pergi, karena kolom tergantung padanya.

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

... dan tambahkan kolom lagi, kali ini sebagai IDENTITY, bersama dengan kunci utama:

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

Di sinilah mulai menarik. Anda dapat mengaktifkan IDENTITY_INSERTpada tabel, yang berarti bahwa Anda dapat secara manual menentukan nilai-nilai IDENTITYkolom ketika Anda memasukkan baris baru (meskipun tidak memperbarui baris yang ada).

SET IDENTITY_INSERT dbo.ident_test ON;

Dengan set itu, DELETEsemua baris dalam tabel, tetapi baris yang Anda hapus berada OUTPUTdi tabel yang sama - tetapi dengan nilai spesifik untuk idkolom (dari kolom cadangan).

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

Setelah selesai, balikkan IDENTITY_INSERT kembali.

SET IDENTITY_INSERT dbo.ident_test OFF;

Jatuhkan kolom sementara yang kami tambahkan:

ALTER TABLE dbo.ident_test DROP COLUMN id_temp;

Dan akhirnya, masukkan kembali IDENTITYkolom, sehingga catatan berikutnya idakan dilanjutkan setelah angka tertinggi yang ada diid kolom:

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

Memeriksa tabel contoh, yang tertinggi id adalah 20.

SELECT * FROM dbo.ident_test;

Tambahkan baris lain dan periksa yang baru IDENTITY :

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

Pada contoh, baris baru akan ditampilkan id=21 . Akhirnya, jika Anda senang, lakukan transaksi:

COMMIT TRANSACTION;

Penting

Ini bukan operasi sepele, dan itu membawa beberapa risiko yang harus Anda ketahui.

  • Lakukan ini di lingkungan uji khusus. Miliki cadangan. :)

  • Saya suka menggunakan BEGIN/COMMIT TRANSACTION karena mencegah proses lain dari mengacaukan meja saat Anda sedang mengubahnya, dan itu memberi Anda kemungkinan untuk mengembalikan semuanya jika terjadi kesalahan. Namun, proses lain yang mencoba mengakses tabel Anda sebelum Anda melakukan transaksi Anda akan berakhir menunggu. Ini mungkin sangat buruk jika Anda memiliki meja besar dan / atau Anda berada di lingkungan produksi.

  • OUTPUT .. INTOtidak akan berfungsi jika tabel target Anda memiliki batasan kunci asing atau salah satu dari sejumlah fitur lain yang saya tidak ingat dari atas kepala saya. Sebagai gantinya Anda bisa memuat data ke dalam tabel sementara, dan kemudian memasukkannya kembali ke tabel asli. Anda mungkin dapat menggunakan peralihan partisi (bahkan jika Anda tidak menggunakan partisi).

  • Jalankan pernyataan ini satu per satu, bukan sebagai batch atau dalam prosedur tersimpan.

  • Coba pikirkan hal-hal lain yang mungkin bergantung pada idkolom yang Anda jatuhkan dan ciptakan kembali. Setiap indeks harus dijatuhkan dan dibuat kembali (seperti yang kami lakukan dengan kunci utama). Ingat untuk menuliskan setiap indeks dan batasan yang harus Anda buat ulang sebelumnya.

  • Nonaktifkan apa pun INSERTdan DELETEmemicu di atas meja.

Jika membuat ulang tabel adalah suatu opsi:

Jika membuat ulang tabel adalah opsi untuk Anda, semuanya jauh lebih sederhana:

  • Buat tabel kosong, dengan id kolom sebagaiIDENTITY ,
  • Set IDENTITY_INSERT ON untuk meja,
  • Isi tabel,
  • Set IDENTITY_INSERT OFF , dan
  • Mempelajari kembali identitas.
Daniel Hutmacher
sumber
Jawaban yang bagus, terima kasih banyak! Memang dalam kasus saya, saya hanya dapat mengatur IDENTITY_INSERT ON, mengisi dan menonaktifkannya. Itu yang ingin saya lakukan, tetapi tidak tahu MSSQL mendukungnya.
Hikari
5

Menggunakan UPDATE, DELETE atau INSERT untuk memindahkan data dapat menghabiskan banyak waktu dan menggunakan sumber daya (IO) pada data dan file log / disk. Dimungkinkan untuk menghindari mengisi log transaksi dengan berpotensi banyak catatan saat mengerjakan tabel besar: Menggunakan pergantian partisi, hanya metadata yang diubah.

Tidak ada pergerakan data yang terlibat dan karena itu dilakukan dengan sangat cepat (hampir seketika).

Tabel sampel

Pertanyaannya tidak menunjukkan tabel asli DDL. DDL berikut akan digunakan sebagai contoh dalam jawaban ini:

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

Setengah lusin id acak dummy dari 0 hingga 15 ditambahkan dengan kueri ini:

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

Contoh data dalam 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

Meja baru dengan IDENTITY(0, 1)

Satu-satunya masalah idTadalah kurangnya IDENTITY(0, 1)properti pada id. Tabel baru dengan struktur yang sama dan IDENTITY(0, 1)dibuat:

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

Selain itu IDENTITY(0, 1), idT_Switchidentik dengan idT.

Kunci Asing

Kunci asing pada idTharus dihapus untuk memungkinkan teknik ini digunakan.

Sakelar Partisi

The idTdan idT_Switchtabel memiliki struktur yang kompatibel. Alih-alih menggunakan DELETE, UPDATEdan INSERTpernyataan untuk memindahkan baris dari idTke idT_Switchatau pada idTdirinya sendiri, ALTER TABLE ... SWITCHdapat digunakan:

ALTER TABLE dbo.idT
SWITCH TO dbo.idT_Switch;

'Partisi' tunggal PK_idT(seluruh tabel) dipindahkan ke PK_idT_Switch(dan sebaliknya). idTsekarang berisi 0 baris dan idT_Switchmengandung 6 baris.

Anda dapat menemukan daftar lengkap persyaratan kompatibilitas sumber dan tujuan di sini:

Mentransfer Data secara Efisien dengan Menggunakan Partition Switching

Perhatikan penggunaan SWITCHini tidak memerlukan Edisi Perusahaan, karena tidak ada partisi yang eksplisit. Tabel unpartitioned dianggap sebagai tabel dengan satu partisi dari SQL Server 2005 dan seterusnya.

Menggantikan idT

idT sekarang kosong dan tidak berguna dan dapat dijatuhkan:

DROP TABLE idT;

idT_Switchdapat diubah namanya dan akan menggantikan idTtabel lama :

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

Kunci asing

Kunci asing dapat ditambahkan lagi ke idTtabel baru . Apa pun yang sebelumnya dihapus dari idTuntuk membuat tabel yang kompatibel untuk beralih juga perlu diulang.

Dipasang kembali

SELECT IDENT_CURRENT( 'dbo.idT');

Perintah ini mengembalikan 0. Tabel idT berisi 6 baris dengan MAX (id) = 15. DBCC CHECKIDENT (table_name) dapat digunakan:

DBCC CHECKIDENT ('dbo.idT');

Karena 15 lebih besar dari 0, maka akan secara otomatis ditampilkan kembali tanpa mencari MAX (id):

Jika nilai identitas saat ini untuk sebuah tabel kurang dari nilai identitas maksimum yang disimpan di kolom identitas, itu diatur ulang menggunakan nilai maksimum di kolom identitas. Lihat bagian 'Pengecualian' yang mengikuti.

IDENT_CURRENT sekarang mengembalikan 15 .

Uji dan tambahkan data

INSERTPernyataan sederhana :

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

Menambahkan baris ini:

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

The idkolom sekarang menggunakan identitas dan nilai yang baru dimasukkan memang 16 (15 + 1).

Informasi lebih lanjut

Ada pertanyaan dan jawaban terkait dengan latar belakang lebih lanjut tentang SWITCHteknik di sini:

Mengapa menghapus properti Identity pada kolom tidak didukung

Julien Vavasseur
sumber
4

Jika Anda ingin memulai dengan nilai identitas baru, Anda perlu memasukkan kembali identitas Anda. Lihat dokumentasi untukCHECKIDENT

DBCC CHECKIDENT (yourtable, reseed, starting point)
Tom V - coba topanswers.xyz
sumber
0

Aktifkan dan Nonaktifkan IDENTITY_INSERT

Jika meja Anda TABLE_A, maka

  1. CREATE TABLE TABLE_B mirip dengan TABLE_A dengan kolom identitas
  2. SET IDENTITY_INSERT TABLE_B ON
  3. INSERT ke TABLE_B dari TABLE_A
  4. SET OFF IDENTITY_INSERT TABLE_B
  5. DROP TABLE TABLE_A dan ganti nama tabel B Exec sp_rename 'TABLE_B', 'TABLE_A'
pengguna4321
sumber