เพิ่มการรวมอัตโนมัติลงใน PK ที่มีอยู่


14

ฉันสร้างตารางในฐานข้อมูลที่มีอยู่แล้วในฐานข้อมูลอื่น เริ่มแรกมีการเติมข้อมูลฐานข้อมูลเก่า PK ของตารางจะต้องได้รับค่าที่มีอยู่แล้วในบันทึกเหล่านั้นดังนั้นจึงไม่สามารถสร้างอัตโนมัติได้

ตอนนี้ฉันต้องการตารางใหม่เพื่อให้มี PK เป็น autoincrement แต่ฉันจะทำอย่างนั้นหลังจากที่มี PK อยู่แล้วและมีข้อมูลได้อย่างไร


3
เมื่อคุณพูดว่า "autoincrement" สิ่งที่ว่าคุณหมายถึง? ใน SQL Server ไม่มีคุณสมบัติดังกล่าวสำหรับคอลัมน์ คุณหมายถึงIDENTITYอะไร
Max Vernon

ใช่นั่นคือวิธีที่เรียกว่าใน MSSQL โดยทั่วไปแล้วฐานข้อมูลนั้นเป็น PK อัตโนมัติ
ฮิคาริ

คำตอบ:


14

วิธีที่ฉันเข้าใจคำถามของคุณคือคุณมีตารางที่มีคอลัมน์ที่มีอยู่จนถึงตอนนี้ที่มีค่าด้วยตนเองและตอนนี้คุณต้องการ (1) ทำให้คอลัมน์นี้เป็นIDENTITYคอลัมน์และ (2) ตรวจสอบให้แน่ใจว่าการIDENTITYเริ่มต้น จากค่าล่าสุดในแถวที่มีอยู่

ก่อนอื่นให้ทดสอบข้อมูลเพื่อเล่นกับ:

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

มีเป้าหมายที่จะทำให้คอลัมน์คีย์ของตารางหลักidเป็นIDENTITYคอลัมน์ที่จะเริ่มต้นที่ 21 ระเบียนถัดไปที่ได้รับการแทรก สำหรับตัวอย่างนี้คอลัมน์xyzแสดงถึงคอลัมน์อื่น ๆ ทั้งหมดของตาราง

ก่อนที่คุณจะทำอะไรโปรดอ่านคำเตือนที่ด้านล่างของโพสต์นี้

ก่อนอื่นในกรณีที่มีบางอย่างผิดปกติ:

BEGIN TRANSACTION;

ตอนนี้ให้เพิ่มคอลัมน์งานชั่วคราวid_tempและตั้งค่าคอลัมน์นั้นidเป็นค่าของคอลัมน์ที่มีอยู่:

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

ต่อไปเราต้องวางidคอลัมน์ที่มีอยู่(คุณไม่สามารถเพียงแค่ "เพิ่ม" IDENTITYไปยังคอลัมน์ที่มีอยู่คุณต้องสร้างคอลัมน์เป็นIDENTITY) คีย์หลักต้องไปด้วยเนื่องจากคอลัมน์ขึ้นอยู่กับมัน

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

... และเพิ่มคอลัมน์อีกครั้งคราวนี้เป็นIDENTITYพร้อมกับคีย์หลัก:

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

นี่คือสิ่งที่มันน่าสนใจ คุณสามารถเปิดใช้งานIDENTITY_INSERTบนโต๊ะซึ่งหมายความว่าคุณสามารถกำหนดค่าของIDENTITYคอลัมน์ได้ด้วยตนเองเมื่อคุณแทรกแถวใหม่ (ไม่ได้อัปเดตแถวที่มีอยู่)

SET IDENTITY_INSERT dbo.ident_test ON;

ด้วยชุดนั้นDELETEแถวทั้งหมดในตาราง แต่แถวที่คุณกำลังลบอยู่OUTPUTในตารางเดียวกันมาก - แต่มีค่าเฉพาะสำหรับidคอลัมน์ (จากคอลัมน์สำรอง)

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

เมื่อเสร็จแล้วให้IDENTITY_INSERTปิดอีกครั้ง

SET IDENTITY_INSERT dbo.ident_test OFF;

วางคอลัมน์ชั่วคราวที่เราเพิ่ม:

ALTER TABLE dbo.ident_test DROP COLUMN id_temp;

และในที่สุดให้ทำการIDENTITYคอลัมน์ใหม่ดังนั้นเรคคอร์ดถัดไปidจะกลับมาทำงานต่อหลังจากตัวเลขที่มีอยู่สูงสุดในidคอลัมน์:

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

ตรวจสอบตารางตัวอย่างidจำนวนสูงสุดคือ 20

SELECT * FROM dbo.ident_test;

เพิ่มแถวอื่นและตรวจสอบใหม่IDENTITY:

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

id=21ในตัวอย่างแถวใหม่จะมี สุดท้ายถ้าคุณมีความสุขให้ทำธุรกรรม:

COMMIT TRANSACTION;

สำคัญ

นี่ไม่ใช่การดำเนินการเล็กน้อยและมีความเสี่ยงค่อนข้างน้อยที่คุณควรระวัง

  • ทำสิ่งนี้ในสภาพแวดล้อมการทดสอบเฉพาะ มีการสำรองข้อมูล :)

  • ฉันชอบที่จะใช้BEGIN/COMMIT TRANSACTIONเพราะมันช่วยป้องกันไม่ให้กระบวนการอื่นยุ่งกับตารางในขณะที่คุณกำลังเปลี่ยนมันและมันจะช่วยให้คุณสามารถย้อนกลับได้ทุกอย่างถ้ามีอะไรผิดปกติ อย่างไรก็ตามกระบวนการอื่น ๆ ที่พยายามเข้าถึงตารางของคุณก่อนที่คุณจะทำธุรกรรมของคุณจะสิ้นสุดการรอ นี่อาจไม่ดีเลยถ้าคุณมีโต๊ะขนาดใหญ่และ / หรือคุณอยู่ในสภาพแวดล้อมการผลิต

  • OUTPUT .. INTOจะไม่ทำงานหากตารางเป้าหมายของคุณมีข้อ จำกัด ของรหัสต่างประเทศหรือคุณสมบัติอื่น ๆ จำนวนหนึ่งที่ฉันจำไม่ได้จากส่วนหัว คุณสามารถแทนที่การโหลดข้อมูลลงในตารางชั่วคราวแทนแล้วใส่กลับเข้าไปในตารางเดิม คุณอาจสามารถใช้การสลับพาร์ติชัน (แม้ว่าคุณจะไม่ได้ใช้พาร์ติชัน)

  • เรียกใช้คำสั่งเหล่านี้ทีละรายการไม่ใช่แบบแบตช์หรือในกระบวนงานที่เก็บไว้

  • ลองนึกถึงสิ่งอื่น ๆ ที่อาจขึ้นอยู่กับidคอลัมน์ที่คุณกำลังดร็อปและสร้างใหม่ ดัชนีใด ๆ จะต้องถูกดร็อปและสร้างใหม่ (เช่นเดียวกับที่เราทำกับคีย์หลัก) อย่าลืมสคริปต์ทุกดัชนีและข้อ จำกัด ที่คุณจะต้องสร้างใหม่ก่อน

  • ปิดการใช้งานใด ๆINSERTและDELETEทริกเกอร์บนโต๊ะ

หากการสร้างตารางใหม่เป็นตัวเลือก:

หากการสร้างตารางใหม่เป็นตัวเลือกสำหรับคุณทุกอย่างง่ายขึ้นมาก:

  • สร้างตารางที่ว่างเปล่าที่มีidคอลัมน์เป็นIDENTITY,
  • กำหนดIDENTITY_INSERT ONไว้สำหรับตาราง
  • เติมตาราง
  • ตั้งค่าIDENTITY_INSERT OFFและ
  • กำหนดค่าตัวตนใหม่

คำตอบที่ดีขอบคุณมาก! ในกรณีของฉันฉันสามารถตั้งค่าIDENTITY_INSERT ONเติมและปิดใช้งานได้ นั่นคือสิ่งที่ฉันต้องการจะทำ แต่ไม่รู้ว่าสนับสนุน MSSQL หรือไม่
ฮิคาริ

5

การใช้ UPDATE, DELETE หรือ INSERT เพื่อย้ายข้อมูลอาจใช้เวลาค่อนข้างนานและใช้ทรัพยากร (IO) ทั้งข้อมูลและล็อกไฟล์ / ดิสก์ มีความเป็นไปได้ที่จะหลีกเลี่ยงการกรอกบันทึกธุรกรรมที่มีจำนวนระเบียนที่อาจเกิดขึ้นในขณะที่ทำงานบนตารางขนาดใหญ่: การใช้การสลับพาร์ติชันเฉพาะข้อมูลเมตาเท่านั้นที่เปลี่ยนไป

ไม่มีการเคลื่อนไหวของข้อมูลที่เกี่ยวข้องดังนั้นจึงดำเนินการได้อย่างรวดเร็ว (เกือบจะทันที)

ตารางตัวอย่าง

คำถามไม่แสดงตารางต้นฉบับ DDL DDL ต่อไปนี้จะใช้เป็นตัวอย่างในคำตอบนี้:

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

มีการเพิ่มรหัสสุ่ม dummy ครึ่งโหลจาก 0 ถึง 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

ตัวอย่างข้อมูลใน 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

สร้างตารางใหม่ด้วย IDENTITY(0, 1)

ปัญหาเดียวกับidTคือการขาดIDENTITY(0, 1)คุณสมบัติใน id ตารางใหม่ที่มีโครงสร้างคล้ายกันและIDENTITY(0, 1)ถูกสร้างขึ้น:

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

นอกเหนือจากIDENTITY(0, 1), เป็นเหมือนidT_SwitchidT

กุญแจต่างประเทศ

idTต้องลบกุญแจต่างประเทศเพื่ออนุญาตให้ใช้เทคนิคนี้

สวิตช์พาร์ติชั่น

idTและidT_Switchตารางมีโครงสร้างที่รองรับ แทนการใช้DELETE, UPDATEและINSERTงบที่จะย้ายแถวจากidTไปidT_SwitchหรือในidTตัวเองALTER TABLE ... SWITCHสามารถนำมาใช้:

ALTER TABLE dbo.idT
SWITCH TO dbo.idT_Switch;

'พาร์ติชัน' เดียวของPK_idT(ทั้งตาราง) ถูกย้ายไปที่PK_idT_Switch(และในทางกลับกัน) idTตอนนี้มี 0 แถวและidT_Switchมี 6 แถว

คุณสามารถดูรายการทั้งหมดของแหล่งที่มาและข้อกำหนดความเข้ากันได้ของปลายทางที่นี่:

การถ่ายโอนข้อมูลอย่างมีประสิทธิภาพโดยใช้การสลับพาร์ติชัน

หมายเหตุการใช้งานSWITCHนี้ไม่ต้องการ Enterprise Edition เพราะไม่มีการแบ่งพาร์ติชันอย่างชัดเจน ตารางที่ไม่แบ่งพาร์ติชันถือเป็นตารางที่มีพาร์ติชันเดียวจาก SQL Server 2005 เป็นต้นไป

แทนที่ idT

idT ตอนนี้ว่างเปล่าและไร้ประโยชน์และสามารถทิ้ง:

DROP TABLE idT;

idT_Switchสามารถเปลี่ยนชื่อและจะแทนที่idTตารางเก่า:

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

กุญแจต่างประเทศ

สามารถเพิ่มกุญแจต่างประเทศได้อีกครั้งในidTตารางใหม่ สิ่งอื่นใดที่ถูกลบไปก่อนหน้านี้idTเพื่อให้ตารางที่เข้ากันได้สำหรับการสลับจะต้องทำซ้ำ

reseed

SELECT IDENT_CURRENT( 'dbo.idT');

คำสั่งนี้ส่งคืน 0 ตาราง idT มี 6 แถวพร้อม MAX (id) = 15. สามารถใช้DBCC CHECKIDENT (table_name) :

DBCC CHECKIDENT ('dbo.idT');

เนื่องจาก 15 มีค่ามากกว่า 0 จึงจะดำเนินการใหม่โดยอัตโนมัติโดยไม่ต้องค้นหา MAX (id):

หากค่าข้อมูลประจำตัวปัจจุบันสำหรับตารางน้อยกว่าค่าข้อมูลประจำตัวสูงสุดที่เก็บไว้ในคอลัมน์ข้อมูลระบุค่าจะถูกรีเซ็ตโดยใช้ค่าสูงสุดในคอลัมน์ข้อมูลประจำตัว ดูส่วน 'ข้อยกเว้น' ที่ตามมา

IDENT_CURRENT ตอนนี้ผลตอบแทนที่15

ทดสอบและเพิ่มข้อมูล

INSERTคำสั่งง่ายๆ:

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

เพิ่มแถวนี้:

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

idคอลัมน์ตอนนี้ใช้ตัวตนและมูลค่าแทรกใหม่แน่นอน 16 (15 + 1)

ข้อมูลมากกว่านี้

มีคำถามและคำตอบที่เกี่ยวข้องกับพื้นหลังเพิ่มเติมเกี่ยวกับSWITCHเทคนิคที่นี่:

เหตุใดจึงลบคุณสมบัติเอกลักษณ์ในคอลัมน์ไม่ได้รับการสนับสนุน


4

หากคุณต้องการเริ่มด้วยค่าตัวตนใหม่คุณต้องทำการระบุตัวตนของคุณใหม่ ดูเอกสารประกอบสำหรับCHECKIDENT

DBCC CHECKIDENT (yourtable, reseed, starting point)

0

เปิดใช้งานและปิดใช้งาน IDENTITY_INSERT

หากตารางของคุณคือ TABLE_A แล้ว

  1. สร้าง TABLE_B คล้ายกับ TABLE_A ด้วยคอลัมน์ข้อมูลประจำตัว
  2. SET IDENTITY_INSERT TABLE_B เปิด
  3. INSERT เข้าสู่ TABLE_B จาก TABLE_A
  4. SET IDENTITY_INSERT TABLE_B ปิด
  5. DROP Table TABLE_A และเปลี่ยนชื่อตาราง B Exec sp_rename 'TABLE_B', 'TABLE_A'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.