Вот способ, который легко масштабируется до трех связанных таблиц.
Используйте MERGE для вставки данных в таблицы копирования, чтобы вы могли ВЫХОДИТЬ старые и новые значения IDENTITY в управляющую таблицу и использовать их для сопоставления связанных таблиц.
Фактический ответ - просто два оператора создания таблицы и три слияния. Остальное - пример настройки данных и демонтажа.
USE tempdb;
--## Create test tables ##--
CREATE TABLE Customers(
[Id] INT NOT NULL PRIMARY KEY IdENTITY,
[Name] NVARCHAR(200) NOT NULL
);
CREATE TABLE Orders(
[Id] INT NOT NULL PRIMARY KEY IdENTITY,
[CustomerId] INT NOT NULL,
[OrderDate] DATE NOT NULL,
CONSTRAINT [FK_Customers_Orders] FOREIGN KEY ([CustomerId]) REFERENCES [Customers]([Id])
);
CREATE TABLE OrderItems(
[Id] INT NOT NULL PRIMARY KEY IdENTITY,
[OrderId] INT NOT NULL,
[ItemId] INT NOT NULL,
CONSTRAINT [FK_Orders_OrderItems] FOREIGN KEY ([OrderId]) REFERENCES [Orders]([Id])
);
CREATE TABLE Customers2(
[Id] INT NOT NULL PRIMARY KEY IdENTITY,
[Name] NVARCHAR(200) NOT NULL
);
CREATE TABLE Orders2(
[Id] INT NOT NULL PRIMARY KEY IdENTITY,
[CustomerId] INT NOT NULL,
[OrderDate] DATE NOT NULL,
CONSTRAINT [FK_Customers2_Orders2] FOREIGN KEY ([CustomerId]) REFERENCES [Customers2]([Id])
);
CREATE TABLE OrderItems2(
[Id] INT NOT NULL PRIMARY KEY IdENTITY,
[OrderId] INT NOT NULL,
[ItemId] INT NOT NULL,
CONSTRAINT [FK_Orders2_OrderItems2] FOREIGN KEY ([OrderId]) REFERENCES [Orders2]([Id])
);
--== Populate some dummy data ==--
INSERT Customers(Name)
VALUES('Aaberg'),('Aalst'),('Aara'),('Aaren'),('Aarika'),('Aaron'),('Aaronson'),('Ab'),('Aba'),('Abad');
INSERT Orders(CustomerId, OrderDate)
SELECT Id, Id+GETDATE()
FROM Customers;
INSERT OrderItems(OrderId, ItemId)
SELECT Id, Id*1000
FROM Orders;
INSERT Customers2(Name)
VALUES('Zysk'),('Zwiebel'),('Zwick'),('Zweig'),('Zwart'),('Zuzana'),('Zusman'),('Zurn'),('Zurkow'),('ZurheIde');
INSERT Orders2(CustomerId, OrderDate)
SELECT Id, Id+GETDATE()+20
FROM Customers2;
INSERT OrderItems2(OrderId, ItemId)
SELECT Id, Id*1000+10000
FROM Orders2;
SELECT * FROM Customers JOIN Orders ON Orders.CustomerId = Customers.Id JOIN OrderItems ON OrderItems.OrderId = Orders.Id;
SELECT * FROM Customers2 JOIN Orders2 ON Orders2.CustomerId = Customers2.Id JOIN OrderItems2 ON OrderItems2.OrderId = Orders2.Id;
--== ** START ACTUAL ANSWER ** ==--
--== Create Linkage tables ==--
CREATE TABLE CustomerLinkage(old INT NOT NULL PRIMARY KEY, new INT NOT NULL);
CREATE TABLE OrderLinkage(old INT NOT NULL PRIMARY KEY, new INT NOT NULL);
--== Copy Header (Customers) rows and record the new key ==--
MERGE Customers2
USING Customers
ON 1=0 -- we just want an insert, so this forces every row as unmatched
WHEN NOT MATCHED THEN
INSERT (Name) VALUES(Customers.Name)
OUTPUT Customers.Id, INSERTED.Id INTO CustomerLinkage;
--== Copy Detail (Orders) rows using the new key from CustomerLinkage and record the new Order key ==--
MERGE Orders2
USING (SELECT Orders.Id, CustomerLinkage.new, Orders.OrderDate
FROM Orders
JOIN CustomerLinkage
ON CustomerLinkage.old = Orders.CustomerId) AS Orders
ON 1=0 -- we just want an insert, so this forces every row as unmatched
WHEN NOT MATCHED THEN
INSERT (CustomerId, OrderDate) VALUES(Orders.new, Orders.OrderDate)
OUTPUT Orders.Id, INSERTED.Id INTO OrderLinkage;
--== Copy Detail (OrderItems) rows using the new key from OrderLinkage ==--
MERGE OrderItems2
USING (SELECT OrderItems.Id, OrderLinkage.new, OrderItems.ItemId
FROM OrderItems
JOIN OrderLinkage
ON OrderLinkage.old = OrderItems.OrderId) AS OrderItems
ON 1=0 -- we just want an insert, so this forces every row as unmatched
WHEN NOT MATCHED THEN
INSERT (OrderId, ItemId) VALUES(OrderItems.new, OrderItems.ItemId);
--== ** END ACTUAL ANSWER ** ==--
--== Display the results ==--
SELECT * FROM Customers2 JOIN Orders2 ON Orders2.CustomerId = Customers2.Id JOIN OrderItems2 ON OrderItems2.OrderId = Orders2.Id;
--== Drop test tables ==--
DROP TABLE OrderItems;
DROP TABLE OrderItems2;
DROP TABLE Orders;
DROP TABLE Orders2;
DROP TABLE Customers;
DROP TABLE Customers2;
DROP TABLE CustomerLinkage;
DROP TABLE OrderLinkage;
Когда я делал это в прошлом, я делал это примерно так:
Резервное копирование обеих баз данных.
Скопируйте строки, которые вы хотите переместить из первой БД во вторую таблицу, без
IDENTITY
столбца.Примечание. Мы будем называть приведенный выше набор таблиц «временным»; тем не менее, я настоятельно рекомендую вам сохранить их в своей собственной базе данных, а также сделать резервную копию, когда вы закончите.
DBCC CHECKIDENT
для смещения следующегоIDENTITY
значения для целевой таблицы на 1 сверх того, что вам нужно для перемещения. Это оставит открытый блокIDENTITY
значений X, который вы можете назначить строкам, перенесенным из первой базы данных.IDENTITY
значение для строк из первой БД и новое значение, которое они будут использовать во второй БД.Пример: вы перемещаете 473 строки, которым потребуется новое
IDENTITY
значение из первой базы данных во вторую. Во-первыхDBCC CHECKIDENT
, следующее значение идентификатора для этой таблицы во второй базе данных сейчас - 1128. ИспользуйтеDBCC CHECKIDENT
для повторного заполнения значения до 1601. Затем вы заполните таблицу сопоставления текущими значениями дляIDENTITY
столбца из родительской таблицы как старые значения и воспользуйтесьROW_NUMBER()
функцией, чтобы назначить числа от 1128 до 1600 в качестве новых значений.Используя таблицу сопоставления, обновите значения в том, что обычно является
IDENTITY
столбцом во временной родительской таблице.SET IDENTITY_INSERT <parent> ON
, вставьте обновленные родительские строки из временной родительской таблицы во вторую БД.ПРИМЕЧАНИЕ. Если некоторые дочерние таблицы имеют
IDENTITY
собственные значения, это становится довольно сложным. Мои настоящие сценарии (частично разработанные поставщиком, поэтому я не могу ими поделиться) имеют дело с десятками таблиц и столбцов первичного ключа, включая те, которые не были автоматически увеличены числовыми значениями. Тем не менее, это основные шаги.После переноса я сохранил таблицы сопоставления, что позволило нам найти «новую» запись на основе старого идентификатора.
Это не для слабонервных, и должны, должны, должны быть проверены (в идеале несколько раз) в тестовой среде.
ОБНОВЛЕНИЕ: Я должен также сказать, что, даже с этим, я не слишком беспокоился о «потере» значений ID. Я фактически установил свои блоки идентификаторов во второй базе данных так, чтобы они были на 2-3 значения больше, чем мне было нужно, чтобы убедиться, что я случайно не столкнусь с существующими значениями.
Я, конечно, понимаю, что не хочу пропустить сотни тысяч потенциальных действительных идентификаторов во время этого процесса, особенно если этот процесс будет повторен (в конечном итоге мой был запущен всего около 20 раз в течение 30 месяцев). При этом, как правило, нельзя полагаться на то, что значения идентификатора автоматического увеличения будут последовательными без пропусков. Когда строка создается и откатывается, значение автоинкремента для этой строки исчезает; следующая добавленная строка будет иметь следующее значение, а та из откатной строки будет пропущена.
источник
Customer-Order-OrderItem
илиCountry-State-City
. Три таблицы, сгруппированные вместе, являются автономными.Я использую таблицу из
WideWorldImporters
базы данных, которая представляет собой новый образец базы данных от Microsoft. Таким образом, вы можете запустить мой скрипт как есть. Вы можете скачать резервную копию этой базы данных здесь .Исходная таблица (существует в образце с данными).
Таблица назначения:
Сейчас делаем экспорт без значения идентификатора столбца. Обратите внимание, что я не вставляю в столбец идентификаторов,
VehicleTemperatureID
а также не выбираю из этого же.Чтобы ответить на второй вопрос об ограничениях ФК, см. Этот пост. Особенно раздел ниже.
источник