SQL Server kopiëren deel van tabellen naar een andere database

Wellicht dat u dit nog nooit aan de hand heeft gehad. Deze week werd ik geconfronteerd met het verzoek om uit een bestaande database een deel van de tabellen te halen en in een aparte database te plaatsen. Dit ging om een deel van productiedata die als input zou dienen voor een omgeving van waaruit presentatie gegeven konden geven. Dat bleek een grotere uitdaging dan ik had verwacht.

Mijn planning voorzag in het gebruik van de import & export wizard van SQL Server Management, aanvinken van ‘Enable identity insert’ en klaar is Kees. Dat bleek iets te kort door de bocht. Bij de import & export wizard worden uitsluitend tabellen en hun data overgezet; niet de constraints en andere tabeleigenschappen. Voor mijn opdracht was dat een probleem: de gehele tabelstructuur inclusief eventuele identity columns moesten worden overgezet. Uiteindelijk is het probleem op de volgende manier opgelost:

* Linked Server gelegd tussen de SQL Server waar de brondatabase op staat en de SQL Server waar de doeldatabase is gehuisvest
– Als alternatief kan hier ook een backup/restore naar dezelfde server als de doeldatabase worden gebruikt, maar verschillen in SQL Server versies kan hier een extra uitdaging in worden
* Vervolgens heb ik met een CURSOR op sys.tables een selectie gemaakt van de tabellen die ik wilde overzetten
* Tenslotte heb ik een T-SQL script door de CURSOR laten lopen en met een SELECT … INTO … de data overgezet.

Waarom dit werkt
SELECT … INTO … zorgt ervoor dat alle tabellen inclusief IDENTITY kolommen wordt aangemaakt. Precies de oplossing waar ik naar op zoek was.

Het is mij een raadsel waarom Microsoft het niet mogelijk maakt om tabellen aan te maken inclusief o.a. IDENTITY kolommen. Redgate lijkt met deze software in deze behoefte te voorzien. Voor het kopiëren van een deel van de tabellen vanuit een andere database werkt mijn script prima.

De T-SQL code

/*
Auteur : Mickel Reemer
Datum : 17 juli 2016

Doel van het script:
Kopiëren van data van één database naar een andere, rekeninghoudend
met onder andere IDENTITY kolommen

Achtergrond:
Bij de import/export wizard van SQL Server wordt geen rekening met IDENTITY
kolommen gehouden. Met andere woorden: heeft een tabel een IDENTITY kolom,
dan komt deze niet als IDENTITY kolom in de doeldatabase terecht. Dit is niet
altijd gewenst. Er kunnen situaties zijn waarbij de IDENTITY kolom(men) wél
in de doeldatabase moeten worden overgenomen.

Uitwerking:
Het script werkt op de volgende manier:
- Er moet verbinding worden gemaakt met de database waar de doelserver op is gehuisvest
- Het script werkt vanuit de brondatabase (deze moet op TWEE plaatsen worden ingesteld)
- Er worden parameters ingesteld voor o.a. bron- en doeldatabase
- Vervolgens worden uit sys.tables de bestaande tabellen in de brondatabase langsgelopen
- Eventueel kunnen tabellen in deze sectie worden uitgesloten via de WHERE clause
- De te kopiëren tabellen worden in een CURSOR geplaatst
- Er wordt een SELECT * INTO ... uitgevoerd vanuit de brontabel, zodat de tabelstructuur uit de brondatabase en de data in de doeldatabase terechtkomt
- Daarna wordt de volgende tabel verwerkt
*/

-- Parameters voor bron- en doeldatabase (stel ze eventueel anders in)
DECLARE @SOURCESERVER nvarchar(255) = 'Server waar de brondatabase op gehuisvest is'
DECLARE @SOURCEDATABASE nvarchar(255) = 'Brondatabase'
DECLARE @TARGETDATABASE nvarchar(255) = 'Doeldatabase'

DECLARE @SQLStatement nvarchar(max) = '' -- Parameter om SQL Statements met parameters samen te stellen
DECLARE @FIELDNAME nvarchar(255) -- Ophalen van veldnamen per veld
DECLARE @TABLENAME nvarchar(255) -- Ophalen van tabellen per tabel
DECLARE @FIELDS nvarchar(max) = '' -- Set van veldnamen per tabel (wordt bij INSERT gebruikt)
DECLARE @DBTableObject nvarchar(255) -- Dit is de tabelnaam inclusief schema
DECLARE @IS_IDENTITY bit = 0 -- Wordt gebruikt bij ophalen van is_identity kolom in sys.columns
DECLARE @SET_IDENTITY bit = 0 -- Wordt gebruikt bij bepaling of IDENTITY_INSERT nodig is

-- Samenstellen van de lijst met tabellen die moeten worden overgezet. Bij de WHERE clause kan dit worden getweaked
DECLARE TABLELIST CURSOR
FOR
SELECT
tbl.name,
'[' + schm.name + '].['+ tbl.name + ']' AS DBTableObject
FROM [Bronserver].[Brondatabase].[sys].[tables] tbl -- LET OP: HIER OOK DE BRONDATABASE (en BRONSERVER) INGEVEN!
JOIN [Bronserver].[Brondatabase].[sys].[schemas] schm on tbl.schema_id = schm.schema_id -- LET OP: HIER OOK DE BRONDATABASE (en BRONSERVER) INGEVEN!
WHERE
1=1
AND tbl.name NOT IN (
'Tabelnaam1',
'Tabelnaam2',
'Tabelnaam3'
)
AND tbl.name NOT LIKE 'BCK%' -- dit zijn vaak backup tabellen
ORDER BY tbl.name -- changelog v1.1 (volgorde van tabellen is nu van A tot Z)

-- Openen van de gevonden lijst met database tabellen die moeten worden overgezet
OPEN TABLELIST
FETCH NEXT FROM TABLELIST INTO @TABLENAME, @DBTableObject

WHILE @@FETCH_STATUS = 0
BEGIN

-- SQL Statement samenstellen met alle benodigde acties
SET @SQLStatement = @SQLStatement +
'-- Processing table ... ' + @DBTableObject + CHAR(13)
SET @SQLStatement = @SQLStatement +
'SELECT * INTO [' + @TARGETDATABASE + '].[dbo].[' + @TABLENAME + ']' + -- Tabelstructuur in doeldatabase bouwen
' FROM [' + @SOURCESERVER + '].[' + @SOURCEDATABASE + '].' + @DBTableObject
SET @SQLStatement = @SQLStatement + CHAR(13)

PRINT @SQLStatement -- Laten zien welke acties er plaatsvinden
EXEC sp_executesql @SQLStatement -- acties daadwerkelijk uitvoeren

PRINT 'Table ' + @TABLENAME + ' processed!' + CHAR(13) -- Tonen wanneer actie gereed is

SET @SQLStatement = '' -- Nulstellen parameters
SET @FIELDS = ''
SET @SET_IDENTITY = 0
FETCH NEXT FROM TABLELIST INTO @TABLENAME, @DBTableObject

END

CLOSE TABLELIST
DEALLOCATE TABLELIST