Добавить автоинкремент к существующему ПК

14

Я создал таблицу в БД, которая уже существует в другой БД. Первоначально он был заполнен старыми данными БД. ПК таблицы должен получать значения, которые уже существуют в этих записях, поэтому он не может быть автоинкрементным.

Теперь мне нужно, чтобы новая таблица имела свой PK в качестве автоинкремента. Но как я могу это сделать после того, как ПК уже существует и у него есть данные?

Hikari
источник
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 существующий столбец, вы должны создать столбец как 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)свойства идентификатора. Новая таблица с аналогичной структурой и 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чтобы сделать таблицы совместимыми для переключения, также должно быть переделано.

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технике здесь:

Почему удаление свойства Identity для столбца не поддерживается

Жюльен Вавассер
источник
4

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

DBCC CHECKIDENT (yourtable, reseed, starting point)
Том V - попробуйте topanswers.xyz
источник
0

ENABLE и DISABLE IDENTITY_INSERT

Если ваша таблица TABLE_A, то

  1. CREATE TABLE TABLE_B аналогично TABLE_A со столбцом идентификаторов
  2. SET IDENTITY_INSERT TABLE_B ON
  3. INSERT в TABLE_B из TABLE_A
  4. SET IDENTITY_INSERT TABLE_B OFF
  5. DROP TABLE TABLE_A и переименовать таблицу B Exec sp_rename 'TABLE_B', 'TABLE_A'
user4321
источник