Voici un moyen qui s'adapte facilement à trois tableaux associés.
Utilisez MERGE pour insérer les données dans les tables de copie afin de pouvoir SORTIR les anciennes et nouvelles valeurs IDENTITY dans une table de contrôle et les utiliser pour le mappage des tables associées.
La réponse réelle est juste deux instructions de création de table et trois fusions. Le reste est un exemple de configuration et de démontage des données.
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;
Quand je l'ai fait dans le passé, je l'ai fait quelque chose comme ça:
Sauvegardez les deux bases de données.
Copiez les lignes que vous souhaitez déplacer de la première base de données vers la seconde dans une nouvelle table, sans
IDENTITY
colonne.Remarque: Nous désignerons l'ensemble de tableaux ci-dessus comme "temporaire"; cependant, je vous recommande fortement de les stocker dans leur propre base de données et de les sauvegarder également lorsque vous avez terminé.
DBCC CHECKIDENT
pour déplacer laIDENTITY
valeur suivante de la table cible à 1 au-delà de ce dont vous avez besoin pour le déplacement. Cela laissera un bloc ouvert deIDENTITY
valeurs X que vous pouvez attribuer aux lignes importées de la première base de données.IDENTITY
valeur des lignes du premier DB et la nouvelle valeur qu'elles utiliseront dans le deuxième DB.Exemple: vous déplacez 473 lignes qui auront besoin d'une nouvelle
IDENTITY
valeur de la première base de données à la seconde. ParDBCC CHECKIDENT
, la prochaine valeur d'identité pour cette table dans la deuxième base de données est actuellement 1128. UtilisezDBCC CHECKIDENT
pour réaménager la valeur à 1601. Vous remplissez ensuite votre table de mappage avec les valeurs actuelles de laIDENTITY
colonne de votre table parent en tant qu'anciennes valeurs et utilisez laROW_NUMBER()
fonction pour attribuer les nombres 1128 à 1600 comme nouvelles valeurs.À l'aide de la table de mappage, mettez à jour les valeurs dans ce qui est généralement la
IDENTITY
colonne de la table parent temporaire.SET IDENTITY_INSERT <parent> ON
, insérez les lignes parent mises à jour de la table parent temporaire dans la deuxième base de données.Remarque: si certaines des tables enfants ont
IDENTITY
leurs propres valeurs, cela devient assez compliqué. Mes scripts réels (partiellement développés par un fournisseur, donc je ne peux pas vraiment les partager) traitent des dizaines de tables et de colonnes de clé primaire, y compris certaines qui n'étaient pas des valeurs numériques à incrémentation automatique. Cependant, ce sont les étapes de base.J'ai conservé les tables de mappage, post-migration, qui avaient l'avantage de nous permettre de trouver un "nouveau" enregistrement basé sur un ancien ID.
Ce n'est pas pour les faibles de cœur, et doit, doit, doit être testé (idéalement plusieurs fois) dans un environnement de test.
MISE À JOUR: Je dois également dire que, même avec cela, je ne me suis pas trop inquiété du "gaspillage" des valeurs d'identification. En fait, j'ai configuré mes blocs d'identification dans la deuxième base de données pour qu'ils soient 2-3 valeurs plus grandes que ce dont j'avais besoin, pour m'assurer que je ne heurterais pas accidentellement les valeurs existantes.
Je comprends certainement de ne pas vouloir ignorer des centaines de milliers d'ID valides potentiels au cours de ce processus, surtout si le processus sera répété (le mien a finalement été exécuté un total d'environ 20 fois en 30 mois). Cela dit, en général, on ne peut pas compter sur des valeurs d'ID à incrémentation automatique pour être séquentielles sans lacunes. Lorsqu'une ligne est créée et annulée, la valeur d'incrémentation automatique de cette ligne disparaît; la ligne suivante ajoutée aura la valeur suivante et celle de la ligne annulée sera ignorée.
la source
Customer-Order-OrderItem
ouCountry-State-City
. Les trois tables, lorsqu'elles sont regroupées, sont autonomes.J'utilise une table de
WideWorldImporters
base de données qui est la nouvelle base de données exemple de Microsoft. De cette façon, vous pouvez exécuter mon script tel quel. Vous pouvez télécharger une sauvegarde de cette base de données ici .Table source (elle existe dans l'exemple avec les données).
Table de destination:
Exportation en cours sans valeur de colonne d'identité. Notez que je n'insère pas dans la colonne d'identité
VehicleTemperatureID
et que je ne sélectionne pas la même chose.Pour répondre à la seconde de la question sur les contraintes FK, veuillez consulter cet article. Surtout la section ci-dessous.
la source