Додайте автоматичне збільшення до існуючого ПК


14

Я створив таблицю в БД, яка вже існує в іншій БД. Спочатку вона була заповнена старими даними БД. PK таблиці повинен був отримати значення, які вже є в цих записах, тому це не могло бути автоматичним збільшенням.

Тепер мені потрібна нова таблиця, щоб її ПК був автоматичним збільшенням. Але як я можу це зробити після того, як ПК вже існує та має дані?


3
Коли ви говорите "автопосилення", про що саме ви маєте на увазі? У SQL Server немає такого властивості для стовпця. Ви маєте на увазі IDENTITY?
Макс Вернон

Так, це називається в MSSQL. У базі даних загалом це ПК з автоматичним збільшенням.
Хікарі

Відповіді:


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до існуючого стовпця, ви повинні створити стовпчик як an 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);

За допомогою цього запиту додано півдесятка фіктивних ідентифікаторів від 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_Switchідентичний idT.

Іноземні ключі

Іноземні клавіші увімкнено, 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щоб зробити таблиці сумісними для комутації, також потрібно буде переробити.

Переглянуто

SELECT IDENT_CURRENT( 'dbo.idT');

Ця команда повертає 0. Таблиця idT містить 6 рядків з MAX (id) = 15. DBCC CHECKIDENT (таблиця_ім'я) можна використовувати:

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техніку:

Чому видалення властивості Identity у стовпці не підтримується


4

Якщо ви хочете почати з нового значення ідентичності, вам потрібно перезавантажити свою особу. Погляньте на документацію дляCHECKIDENT

DBCC CHECKIDENT (yourtable, reseed, starting point)

0

ENABLE та DISABLE IDENTITY_INSERT

Якщо ваш стіл TABLE_A, тоді

  1. CREATE TABLE TABLE_B схожий на TABLE_A зі стовпцем ідентичності
  2. ВКЛЮЧЕНО Ідентифікатор_INSERT TABLE_B
  3. ВСТАВИТИ в TABLE_B від TABLE_A
  4. Встановити IDENTITY_INSERT TABLE_B OFF
  5. ЗРОЗНІТЬ ТАБЛИЦЮ TABLE_A та перейменуйте таблицю B Exec sp_rename 'TABLE_B', 'TABLE_A'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.