Aquí hay una forma que se escala fácilmente a tres tablas relacionadas.
Use MERGE para insertar los datos en las tablas de copia para que pueda SALIR los valores de IDENTIDAD antiguos y nuevos en una tabla de control y usarlos para la asignación de tablas relacionadas.
La respuesta real es solo dos crear declaraciones de tabla y tres fusiones. El resto es configuración de datos de muestra y desmontaje.
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;
Cuando hice esto en el pasado, hice algo como esto:
Copia de seguridad de ambas bases de datos.
Copie las filas que desea mover del primer DB al segundo en una nueva tabla, sin una
IDENTITY
columna.Nota: Nos referiremos al conjunto de tablas anterior como "temporal"; sin embargo, le recomiendo que los almacene en su propia base de datos y que los respalde también cuando haya terminado.
DBCC CHECKIDENT
para cambiar el siguienteIDENTITY
valor de la tabla de destino a 1 más allá de lo que necesita para el movimiento. Esto dejará un bloque abierto deIDENTITY
valores X que puede asignar a las filas que se traen de la primera base de datos.IDENTITY
valor anterior para las filas del primer DB y el nuevo valor que usarán en el segundo DB.Ejemplo: está moviendo 473 filas que necesitarán un nuevo
IDENTITY
valor de la primera base de datos a la segunda. PorDBCC CHECKIDENT
, el siguiente valor de identidad para esa tabla en la segunda base de datos es 1128 en este momento. UseDBCC CHECKIDENT
para reiniciar el valor a 1601. Luego completará su tabla de mapeo con los valores actuales para laIDENTITY
columna de su tabla principal como valores antiguos, y usará laROW_NUMBER()
función para asignar los números 1128 a 1600 como los nuevos valores.Usando la tabla de mapeo, actualice los valores en lo que generalmente es la
IDENTITY
columna en la tabla primaria temporal.SET IDENTITY_INSERT <parent> ON
, inserte las filas primarias actualizadas de la tabla primaria temporal en la segunda base de datos.NOTA: Si algunas de las tablas secundarias tienen
IDENTITY
sus propios valores, esto se vuelve bastante complicado. Mis scripts reales (parcialmente desarrollados por un proveedor, por lo que realmente no puedo compartirlos) tratan con docenas de tablas y columnas de clave principal, incluidas algunas que no eran valores numéricos de incremento automático. Sin embargo, estos son los pasos básicos.Conservé las tablas de mapeo, después de la migración, que tenían el beneficio de permitirnos encontrar un "nuevo" registro basado en una identificación anterior.
No es para los débiles de corazón, y debe, debe, debe probarse (idealmente varias veces) en un entorno de prueba.
ACTUALIZACIÓN: También debería decir que, incluso con esto, no me preocupé demasiado por "desperdiciar" los valores de ID. De hecho, configuré mis bloques de ID en la segunda base de datos para que sean 2-3 valores más grandes de lo que necesitaba, para intentar asegurarme de que no colisionaría accidentalmente con los valores existentes.
Ciertamente entiendo que no quiero omitir cientos de miles de identificaciones válidas potenciales durante este proceso, especialmente si el proceso se repetirá (el mío finalmente se ejecutó un total de alrededor de 20 veces en el transcurso de 30 meses). Dicho esto, en general, no se puede confiar en que los valores de ID de incremento automático sean secuenciales sin espacios. Cuando se crea y se revierte una fila, el valor de incremento automático para esa fila desaparece; la siguiente fila agregada tendrá el siguiente valor, y se saltará el de la fila revertida.
fuente
Customer-Order-OrderItem
oCountry-State-City
. Las tres tablas, cuando se agrupan, son independientes.Estoy usando una tabla de la
WideWorldImporters
base de datos que es la nueva base de datos de muestra de Microsoft. De esa manera puedes ejecutar mi script tal como está. Puede descargar una copia de seguridad de esta base de datos desde aquí .Tabla fuente (esta existe en una muestra con datos).
Tabla de destino:
Ahora haciendo la exportación sin valor de columna de identidad. Tenga en cuenta que no estoy insertando en la columna de identidad
VehicleTemperatureID
y tampoco seleccionando de la misma.Para responder la segunda pregunta sobre las restricciones de FK, consulte esta publicación. Especialmente la sección a continuación.
fuente