Berikut adalah cara yang mudah menskala ke tiga tabel terkait.
Gunakan MERGE untuk menyisipkan data ke dalam tabel salinan sehingga Anda bisa OUTPUT nilai IDENTITAS lama dan baru ke dalam tabel kontrol dan menggunakannya untuk pemetaan tabel terkait.
Jawaban sebenarnya adalah hanya dua membuat pernyataan tabel dan tiga gabungan. Sisanya adalah contoh pengaturan data dan runtuhkan.
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;
Ketika saya telah melakukan ini di masa lalu, saya melakukannya seperti ini:
Cadangkan kedua database.
Salin baris yang ingin Anda pindahkan dari DB pertama ke yang kedua ke tabel baru, tanpa
IDENTITY
kolom.Catatan: Kami akan merujuk ke set tabel di atas sebagai "sementara"; Namun, saya sangat menyarankan Anda menyimpannya di database mereka sendiri, dan mendukungnya juga ketika Anda selesai.
DBCC CHECKIDENT
untuk menggeser nilai berikutnyaIDENTITY
untuk tabel target ke 1 di luar apa yang Anda butuhkan untuk bergerak. Ini akan meninggalkan blok terbukaIDENTITY
nilai X yang dapat Anda tetapkan untuk baris yang dibawa dari database pertama.IDENTITY
untuk baris membentuk DB pertama, dan nilai baru yang akan mereka gunakan dalam DB kedua.Contoh: Anda memindahkan 473 baris yang akan membutuhkan
IDENTITY
nilai baru dari database pertama ke yang kedua. PerDBCC CHECKIDENT
, nilai identitas berikutnya untuk tabel itu di database kedua adalah 1128 sekarang. GunakanDBCC CHECKIDENT
untuk mengembalikan nilai ke 1601. Kemudian Anda akan mengisi tabel pemetaan Anda dengan nilai saat ini untukIDENTITY
kolom dari tabel induk Anda sebagai nilai lama, dan menggunakanROW_NUMBER()
fungsi untuk menetapkan angka 1128 hingga 1600 sebagai nilai baru.Menggunakan tabel pemetaan, perbarui nilai dalam apa yang biasanya
IDENTITY
kolom di tabel induk sementara.SET IDENTITY_INSERT <parent> ON
, masukkan baris induk yang diperbarui dari tabel induk sementara ke dalam DB kedua.CATATAN: Jika beberapa tabel anak memiliki
IDENTITY
nilai sendiri, ini menjadi cukup rumit. Skrip aktual saya (sebagian dikembangkan oleh vendor, jadi saya tidak bisa membaginya) berurusan dengan lusinan tabel dan kolom kunci utama, termasuk beberapa yang bukan nilai numerik kenaikan otomatis. Namun, ini adalah langkah dasar.Saya mempertahankan tabel pemetaan, pasca migrasi, yang memiliki manfaat memungkinkan kami menemukan catatan "baru" berdasarkan ID lama.
Ini bukan untuk menjadi lemah hati, dan harus, harus, harus diuji (idealnya beberapa kali) di lingkungan pengujian.
UPDATE: Saya juga harus mengatakan bahwa, bahkan dengan ini, saya tidak terlalu khawatir tentang "membuang-buang" nilai ID. Saya benar-benar mengatur blok ID saya di database kedua menjadi 2-3 nilai lebih besar dari yang saya butuhkan, untuk memastikan saya tidak akan secara tidak sengaja bertabrakan dengan nilai yang ada.
Saya tentu mengerti tidak ingin melewatkan ratusan ribu ID potensial yang potensial selama proses ini, terutama jika proses itu akan diulangi (tambang saya akhirnya berjalan total sekitar 20 kali selama 30 bulan). Yang mengatakan, secara umum, seseorang tidak bisa mengandalkan nilai ID kenaikan otomatis untuk berurutan tanpa celah. Ketika sebuah baris dibuat dan digulung kembali, nilai kenaikan otomatis untuk baris itu hilang; baris berikutnya yang ditambahkan akan memiliki nilai berikutnya , dan yang dari baris belakang yang digulirkan akan dilewati.
sumber
Customer-Order-OrderItem
atauCountry-State-City
. Tiga tabel, ketika dikelompokkan bersama, mandiri.Saya menggunakan tabel dari
WideWorldImporters
basis data yang merupakan basis data sampel baru dari Microsoft. Dengan cara itu Anda dapat menjalankan skrip saya apa adanya. Anda dapat mengunduh cadangan dari basis data ini dari sini .Tabel sumber (ini ada dalam sampel dengan data).
Tabel tujuan:
Sekarang melakukan ekspor tanpa nilai kolom identitas. Perhatikan saya tidak memasukkan ke dalam kolom identitas
VehicleTemperatureID
dan juga tidak memilih dari yang sama.Untuk menjawab pertanyaan kedua tentang batasan FK, silakan lihat posting ini . Terutama bagian di bawah ini.
sumber