Migracija podataka uz mapiranje upotrebom MERGE komande
Objavljeno: 28-09-2015 | Autor: Nenad Živković | Kategorija: T-SQL, Trikovi
Ознаке: Merge
0
Prebacivanje podataka iz jedne tabele u drugu je nešto što svaki DB developer radi manje-više redovno. Povremeno se, kod tog procesa, može pojaviti i dodatna komplikacija da svi preneti redovi moraju dobiti nove PK u novoj tabeli. Razlozi za to mogu biti različiti: da u ciljnoj tabeli već postoje redovi koji imaju iste ID-eve kao i u izvornoj, da se razlikuje IDENTITY SEED dve tabele, da se razlikuju tipovi podataka PK-a ili čak da se razlikuje i broj PK kolona. U svakom slučaju, najčešće je potrebno usput sačuvati i vezu između novih i starih ID-eva u nekoj tabeli za mapiranje.
Ovaj članak će opisati rešenje za tu situaciju: Prebacivanje redova iz jedne tabele u drugu, gde se u destinacionoj tabeli dodeljuju novi ID-evi, a pritom čuvamo vezu u trećoj tabeli.
Kao primer uzećemo tabelu dbo.CrtaniFilmovi iz naše baze koja kao primarni ključ ima kolonu CrtacID i pored nje još dve kolone sa opisom i nazivom.
Zadatak bi nam bio da prebacimo podatke o crtanim filmovima u neku tabelu dbo.Cartoons koja generiše svoje ID-eve u CartoonID koloni (po drugačijem seed-u). Usput bi trebalo sačuvati i vezu između postojećih slogova i novih u tabeli dbo.CartoonsCrtaciMapping
CREATE TABLE dbo.Cartoons (CartoonID INT IDENTITY(100,10) , CartoonName NVARCHAR(100)); CREATE TABLE dbo.CartoonsCrtaciMapping (CartoonID INT, CrtacID INT);
Postoji nekoliko načina da se ovo uradi i njih nećemo detaljno opisivati u ovom članku, ali ćemo pomenuti i skrenuti pažnju na njihove nedostatke:
1. While petlja ili kursor koji bi prebacivao red po red, uz pomoć SCOPE_IDENTITY() funkcije čitati novokreirane ID-eve i upisivati ih. Možda naizgled najjednostavnije rešenje, ali već na par hiljada redova postaje neprihvatljivo neperformantno.
2. Dodati privremeno kolonu CrtacID u Cartoons tabelu, popuniti je prilikom inserta i ukloniti nakon prebacivanja podataka u Mapping tabelu. Ovo je relativno OK varijanta, ali samo ukoliko postoji mogućnost altera destinacione tabele, što, usled nedostatka prava ili zbog opasnosti da bi ometalo druge upite, neće uvek biti slučaj.
3. Prebaciti podatke prostim INSERT..SELECT bez razmišljanja o mapiranju, a nakon toga uraditi JOIN između destinacione i izvorne tabele po unique kolonama. Prošlo bi u našem primeru sa kolonama CartoonName i Naziv, ali šta ako nemamo unique kolone? Šta ako u već postojećim podacima postoje podaci identični onim koji se ubacuju? Dosta upitnika i dosta rizična varijanta.
4. Aktivirati IDENTITY_INSERT nad ciljnom tabelom i voditi ručno računa o ključevima ili uz pomoć nekih privremenih tabela. Još jedna riskanta varijanta, a koja usput zahteva i da se tabela zaključa i spreči bilo kakav upis sa strane dok traje migracija.
Jedina logična i bezbedna opcija bila bi korišćenje OUTPUT klauzule prilikom inserta, nešto kao u sledećem primeru:
INSERT INTO dbo.Cartoons OUTPUT INSERTED.CartoonID, CrtaniFilmovi.CrtacID INTO dbo.CartoonsCrtaciMapping SELECT Naziv FROM dbo.CrtaniFilmovi;
Međutim navedeni upit će vratiti grešku, s obzirom da u OUTPUT-u nije moguće koristiti kolone iz SELECT dela već samo one koje su insertovane.
Msg 4104, Level 16, State 1, Line 2
The multi-part identifier „CrtaniFilmovi.CrtacID“ could not be bound.
Problem se prevazilazi tako što se umesto INSERT naredbe može upotrebiti MERGE naredba koja dozvoljava OUTPUT i izvornih podataka. Iako originalna namena MERGE-a nije za prost INSERT, ništa nas ne sprečava da je upotrebimo tako. Samo ćemo za spajanje staviti neki nemogući uslov i u svakom slučaju će se desiti INSERT:
MERGE dbo.Cartoons USING dbo.CrtaniFilmovi ON 1=2 WHEN NOT MATCHED THEN INSERT (CartoonName) VALUES (Naziv) OUTPUT INSERTED.CartoonID, CrtaniFilmovi.CrtacID INTO CartoonsCrtaciMapping;
Izvršavanjem ove naredbe svi crtaći iz naše tabele će biti prebačeni u novu dbo.Cartoons, a usput će i mapping tabela biti popunjena novim i starim ID-evima.
Naravno, ovo je moguće samo ukoliko koristite SQL Server 2008 ili noviji gde postoji komanda MERGE. Ukoliko ste i dalje na 2005 moraćete da primenite neku od gore pomenutih varijanti (2 ili 3 ukoliko je moguće).