Mevcut PK'ye otomatik eklemeyi ekle


14

Zaten başka bir DB'de bulunan bir DB'de bir tablo oluşturdum. Başlangıçta eski DB verileriyle dolduruldu. Tablonun PK'sı bu kayıtlarda zaten var olan değerleri almak zorundaydı, bu yüzden otomatik artış olamazdı.

Şimdi PK'sını otomatik artış olarak göstermesi için yeni tabloya ihtiyacım var. Ama bunu PK zaten mevcut ve veri aldıktan sonra nasıl yapabilirim?


3
"Autoincrement" dediğinde tam olarak ne demek istiyorsun? SQL Server'da bir sütun için böyle bir özellik yoktur. Bunu mu demek istediniz IDENTITY?
Max Vernon

Evet, MSSQL'de böyle denir. Genel olarak veritabanında, bir oto-artış PK'sıdır.
Hikari

Yanıtlar:


14

Sorunuzu anlamanın yolu, şimdiye kadar manuel değerlerle doldurulmuş bir sütunu olan mevcut bir tablonuz olması ve şimdi (1) bu sütunu bir IDENTITYsütun yapmak ve (2) IDENTITYbaşlangıçların mevcut satırlardaki en son değerden

Öncelikle, oynamak için bazı test verileri:

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

Amaç, tablonun birincil anahtar sütunu, yani idbir IDENTITYsonraki eklenen kayıt için 21'de başlayacak bir sütun yapmaktır . Bu örnek için sütun xyz, tablonun diğer tüm sütunlarını temsil eder.

Herhangi bir şey yapmadan önce, lütfen bu yazının altındaki uyarıları okuyun.

Öncelikle, bir şeyler ters giderse:

BEGIN TRANSACTION;

Şimdi geçici bir çalışma sütunu ekleyelim id_tempve bu sütunu mevcut idsütunun değerlerine ayarlayalım :

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

Daha sonra, mevcut idsütunu bırakmamız gerekiyor ( varolan bir sütuna "ekleyemezsiniz" IDENTITY, sütunu bir olarak oluşturmanız gerekir IDENTITY). Birincil anahtar da gitmelidir, çünkü sütun ona bağlıdır.

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

... ve sütunu bu kez IDENTITYbirincil anahtarla birlikte bir olarak ekleyin :

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

İşte burada ilginç oluyor. IDENTITY_INSERTTabloda etkinleştirebilirsiniz , yani IDENTITYyeni satırlar eklerken bir sütunun değerlerini manuel olarak tanımlayabilirsiniz (mevcut satırları güncellemezsiniz).

SET IDENTITY_INSERT dbo.ident_test ON;

Bu DELETEkümeyle, tablodaki tüm satırlar, ancak sildiğiniz satırlar OUTPUTtam olarak aynı tablonun içindedir - ancak idsütun için belirli değerlerle (yedek sütundan).

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

Tamamlandığında, IDENTITY_INSERTtekrar kapatın.

SET IDENTITY_INSERT dbo.ident_test OFF;

Eklediğimiz geçici sütunu bırakın:

ALTER TABLE dbo.ident_test DROP COLUMN id_temp;

Ve son olarak, IDENTITYsütunu yeniden boyutlandırın , böylece bir sonraki kayıt sütundaki idmevcut en yüksek sayıdan sonra devam edecektir id:

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

Örnek tabloya bakıldığında en yüksek idsayı 20'dir.

SELECT * FROM dbo.ident_test;

Başka bir satır ekleyin ve yeni satırını kontrol edin IDENTITY:

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

Örnekte, yeni satır olacaktır id=21. Son olarak, memnunsanız işlemi gerçekleştirin:

COMMIT TRANSACTION;

Önemli

Bu önemsiz bir işlem değildir ve bilmeniz gereken birkaç risk taşır.

  • Bunu özel bir test ortamında yapın. Yedeklemeler var. :)

  • Kullanmayı seviyorum BEGIN/COMMIT TRANSACTIONçünkü değiştirmenin ortasındayken diğer işlemlerin tabloyla uğraşmasını önler ve bir şeyler ters giderse size her şeyi geri alma imkanı verir. Ancak, işleminizi gerçekleştirmeden önce tablonuza erişmeye çalışan diğer işlemler beklemeye başlar. Büyük bir masanız varsa ve / veya üretim ortamındaysanız bu oldukça kötü olabilir.

  • OUTPUT .. INTOhedef tablonuzda yabancı anahtar kısıtlamaları veya başımın üstünden hatırlayamadığım başka özellikler varsa çalışmaz. Bunun yerine verileri geçici bir tabloya yükleyebilir ve ardından özgün tabloya geri ekleyebilirsiniz. Bölüm anahtarlamayı kullanabilirsiniz (bölüm kullanmasanız bile).

  • Bu ifadeleri toplu olarak veya saklı yordamda değil, tek tek çalıştırın.

  • idBıraktığınız ve yeniden oluşturduğunuz sütuna bağlı olabilecek diğer şeyleri düşünmeye çalışın . Tüm dizinler bırakılmalı ve yeniden oluşturulmalıdır (birincil anahtarda yaptığımız gibi). Önceden yeniden oluşturmanız gereken her dizini ve kısıtlamayı yazmayı unutmayın.

  • Tablodaki tüm INSERTve DELETEtetikleyicileri devre dışı bırakın .

Tabloyu yeniden oluşturmak bir seçenekse:

Tabloyu yeniden oluşturmak sizin için bir seçenekse, her şey çok daha basittir:

  • Boş tabloyu, idsütun olarak IDENTITY,
  • IDENTITY_INSERT ONTablo için ayarla ,
  • Masayı doldurun,
  • Ayarla IDENTITY_INSERT OFFve
  • Kimliği yeniden düzenleyin.

Harika cevap, çok teşekkürler! Aslında benim durumumda sadece ayarlayabilir IDENTITY_INSERT ON, doldurabilir ve devre dışı bırakabilirim. Yapmak istediğim bu, ama MSSQL'in bunu desteklediğini bilmiyordum.
Hikari

5

Verileri taşımak için UPDATE, DELETE veya INSERT kullanmak oldukça zaman alabilir ve hem veri hem de günlük dosyalarında / disklerinde kaynakları (IO) kullanabilir. Büyük bir tablo üzerinde çalışırken işlem günlüğünü potansiyel olarak çok sayıda kayıtla doldurmaktan kaçınmak mümkündür: Bölüm değiştirme kullanarak, yalnızca meta veriler değiştirilir.

Herhangi bir veri hareketi söz konusu değildir ve bu nedenle bu gerçekten hızlı bir şekilde gerçekleştirilir (neredeyse anında).

Örnek tablo

Soru, orijinal DDL tablosunu göstermiyor. Bu cevapta örnek olarak aşağıdaki DDL kullanılacaktır:

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

Bu sorguya 0 ile 15 arasında yarım düzine kukla rastgele kimlik eklenir:

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

İçindeki örnek veriler 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

İle yeni tablo IDENTITY(0, 1)

Tek sorun kimliği mülkiyet idTeksikliği IDENTITY(0, 1). Benzer bir yapıya sahip yeni bir tablo IDENTITY(0, 1)oluşturulur:

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

Kenara IDENTITY(0, 1), idT_Switchile aynıdır idT.

Yabancı anahtarlar

idTBu tekniğin kullanılabilmesi için üzerindeki yabancı anahtarların çıkarılması gerekir.

Bölme Anahtarı

idTVe idT_Switchtablolar uyumlu yapıya sahiptir. Bunun yerine kullanmanın DELETE, UPDATEve INSERTsatırları taşımak için ifadeleri idTiçin idT_Switchveya idTkendisi, ALTER TABLE ... SWITCHkullanılabilir:

ALTER TABLE dbo.idT
SWITCH TO dbo.idT_Switch;

PK_idTTüm tablonun tek 'bölümü', PK_idT_Switch(ve tersi) konumuna taşınır . idTartık 0 satır ve idT_Switch6 satır içeriyor.

Kaynak ve hedef uyumluluk gereksinimlerinin tam listesini burada bulabilirsiniz:

Bölüm Değiştirme Kullanarak Verileri Verimli Aktarma

SWITCHAçık bir bölümleme olmadığından, bu kullanımı Enterprise Edition gerektirmez. Bölümlenmemiş bir tablo, SQL Server 2005'ten itibaren tek bir bölüm içeren bir tablo olarak kabul edilir.

değiştirmek idT

idT artık boş ve işe yaramaz ve düşürülebilir:

DROP TABLE idT;

idT_Switchyeniden adlandırılabilir ve eski idTtabloyu değiştirir :

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

Yabancı anahtarlar

Yabancı anahtarlar yeni idTtabloya tekrar eklenebilir . idTTabloları anahtarlama için uyumlu hale getirmek için daha önce kaldırılan diğer her şeyin de yeniden yapılması gerekir.

reseed

SELECT IDENT_CURRENT( 'dbo.idT');

Bu komut 0 döndürür. Tablo idT, MAX (id) = 15 olan 6 satır içerir. DBCC CHECKIDENT (tablo_adı) kullanılabilir:

DBCC CHECKIDENT ('dbo.idT');

15 0'dan büyük olduğu için, MAX (id) araması yapılmadan otomatik olarak yeniden boyutlandırılır:

Bir tablo için geçerli kimlik değeri, kimlik sütununda depolanan maksimum kimlik değerinden küçükse, kimlik sütununda maksimum değer kullanılarak sıfırlanır. Aşağıdaki 'İstisnalar' bölümüne bakın.

IDENT_CURRENT artık 15 değerini döndürüyor .

Veri test etme ve ekleme

Basit bir INSERTifade:

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

Bu satırı ekler:

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

idKolon hemen kimliği kullanarak ve yeni eklenen değer de, 16 (1 + 15).

Daha fazla bilgi

SWITCHBuradaki teknikle ilgili daha fazla bilgi ile ilgili bir soru ve cevap var:

Neden desteklenmeyen bir sütundaki Kimlik özelliğini kaldırma



0

IDENTITY_INSERT ETKİNLEŞTİR ve DEVRE DIŞI BIRAK

Tablonuz TABLE_A ise

  1. CREATE TABLE TABLE_B, kimlik sütunuyla TABLE_A'ya benzer
  2. SET IDENTITY_INSERT TABLO_B AÇIK
  3. TABLE_A konumundan TABLE_B içine ekle
  4. IDENTITY_INSERT TABLO_B KAPALI
  5. DABLE TABLE TABLE_A ve tablo B'yi yeniden adlandır sp_rename 'TABLE_B', 'TABLE_A'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.