Ich möchte eine bestehende MS-SQL Datenbank in einen lokalen Docker Container kopieren.
Dazu werde ich zuerst die Struktur der Datenbank exportieren und in einem Docker Container neu aufbauen. Anschließend werden die Daten in CSV Dateien exportiert und in die containerisierte Datenbank importiert.
Datenbank Script exportieren
Den Export der Datenbankstruktur geschieht über das MS-SQL Management Studio. In der DB einloggen und dann im Objekt-Explorer über Tasks -> Skripts generieren aufrufen:
Die Einstellungen wie vorgegeben belassen und die Datei script.sql speichern.
Das Script beginnt ungefähr so:
USE [master] GO CREATE DATABASE [DEMO] CONTAINMENT = NONE ON PRIMARY ( NAME = N'DEMO', FILENAME = N'C:/Pfad/DEMO.mdf' , SIZE = 102400KB , MAXSIZE = UNLIMITED, FILEGROWTH = 102400KB ), FILEGROUP [DEMO_DAT] ( NAME = N'DEMO_DAT', FILENAME = N'C:/Pfad/DEMO.ndf' , SIZE = 5120KB , MAXSIZE = UNLIMITED, FILEGROWTH = 102400KB ), FILEGROUP [DEMO_IDX] ( NAME = N'DEMO_IDX', FILENAME = N'C:/Pfad/DEMO_IDX.ndf' , SIZE = 3552960KB , MAXSIZE = UNLIMITED, FILEGROWTH = 102400KB ) LOG ON ( NAME = N'DEMO_log', FILENAME = N'C:/Pfad/DEMO_Log.ldf' , SIZE = 2234880KB , MAXSIZE = 2048GB , FILEGROWTH = 102400KB ) GO ALTER DATABASE [DEMO] ADD FILEGROUP [DEMO_DAT] GO ALTER DATABASE [DEMO] ADD FILEGROUP [DEMO_IDX] GO USE [DEMO] GO CREATE USER [DONALDDEMO] FOR LOGIN [DonaldDemo] WITH DEFAULT_SCHEMA=[DEMO_SCHEMA] GO GO ALTER ROLE [db_owner] ADD MEMBER [DONALDDEMO] GO CREATE SCHEMA [DEMO_SCHEMA] GO
Datenbank Script anpassen
Damit das Script im Container ausgeführt werden kann, müssen ein paar Anpassungen erfolgen.
Der Speicherort auf dem Quell-Server lautet: "C:/Pfad/". Im Container lautet der Pfad: "/var/opt/mssql/data/". Dies ist bei den Filenamen anzupassen.
Das Script definiert einen Benutzer für die Datenbank an und weist diesem ein Login zu:
CREATE USER [DONALDDEMO] FOR LOGIN [DonaldDemo]
User und Login sind also zwei verschiedene Sachen. Der User gehört zur Datenbank, der Login zur übergeordneten Datenbankinstanz "master". Bevor über das Script die DB und der User angelegt werden, wird ein entsprechender Login angelegt:
USE [master] GO CREATE LOGIN [DonaldDemo] WITH PASSWORD = 'DonaldDemo12345678'
Eventuell vorhandenen AD-Accounts fliegen raus, die benötige ich nicht für die lokale Entwicklung.
Das angepasste Demo-Script:
USE [master] GO CREATE LOGIN [DonaldDemo] WITH PASSWORD = 'DonaldDemo12345678' USE [master] GO CREATE DATABASE [DEMO] CONTAINMENT = NONE ON PRIMARY ( NAME = N'DEMO', FILENAME = N'/var/opt/mssql/data/DEMO.mdf' , SIZE = 102400KB , MAXSIZE = UNLIMITED, FILEGROWTH = 102400KB ), FILEGROUP [DEMO_DAT] ( NAME = N'DEMO_DAT', FILENAME = N'/var/opt/mssql/data/DEMO.ndf' , SIZE = 5120KB , MAXSIZE = UNLIMITED, FILEGROWTH = 102400KB ), FILEGROUP [DEMO_IDX] ( NAME = N'DEMO_IDX', FILENAME = N'/var/opt/mssql/data/DEMO_IDX.ndf' , SIZE = 3552960KB , MAXSIZE = UNLIMITED, FILEGROWTH = 102400KB ) LOG ON ( NAME = N'DEMO_log', FILENAME = N'/var/opt/mssql/data/DEMO_Log.ldf' , SIZE = 2234880KB , MAXSIZE = 2048GB , FILEGROWTH = 102400KB ) GO ALTER DATABASE [DEMO] ADD FILEGROUP [DEMO_DAT] GO ALTER DATABASE [DEMO] ADD FILEGROUP [DEMO_IDX] GO USE [DEMO] GO CREATE USER [DONALDDEMO] FOR LOGIN [DonaldDemo] WITH DEFAULT_SCHEMA=[DEMO_SCHEMA] GO GO ALTER ROLE [db_owner] ADD MEMBER [DONALDDEMO] GO CREATE SCHEMA [DEMO_SCHEMA] GO
Der Docker Container
Die Quell DB ist ein Microsoft SQL Server Version 11, was dem dem Release Namen "SQL Server 2012" entspricht. Das älteste Docker Image ist ein SQL Server 2017, was der Version 14 entspricht. Bei meinen Tests war es aber kein Problem, dass die DB in eine höhere Version migriert wird.
Um die Datenbank zu persistieren wird ein Docker Volume verwendet und das Image über Docker Compose gestartet.
version: "3.8" services: sql-server-db: container_name: sql-server-db image: microsoft/mssql-server-linux:2017-latest ports: - "1433:1433" environment: SA_PASSWORD: "change_This_Password" ACCEPT_EULA: "Y" volumes: - mssql_vol:/var/opt/mssql volumes: mssql_vol:
Start des MS-SQL Servers:
docker-compose up -d
Stoppen des MS-SQL Servers und bei Bedarf anschließendes Löschen des Volumes, um danach wieder frisch anfangen zu können:
docker-compose down docker volume rm ms-sql_mssql_vol
Sobald der MS-SQL Server gestartet wurde, kann das SQL-Script in den Container kopiert und dort ausgeführt werden:
docker cp demo_script.sql sql-server-db:/var/opt/mssql/demo_script.sql docker exec -it sql-server-db bash root@dockerContainer: /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P $SA_PASSWORD -i /var/opt/mssql/demo_script.sql # -o /var/opt/mssql/demo_script.out
Mit der Datenbank verbinden
Mit dem Microsoft SQL Server Management Studio kann man sich nun gegen die Datenbank verbinden:
- Servername: localhost
- Port: 1433
- Anmeldename: DonaldDemo
- Kennwort: DonaldDemo12345678
Daten Export
Für den Daten ex- und anschließenden import verwende ich ein Tool auf das ich hier nicht weiter eingehen werde (vgl. DB Export & Import) und beschreibe lediglich die logischen Schritte und die benötigten SQLs.
In einem ersten Schritt werden die zu exportierenden Tabellen der benötigten Schemata der Datenbank ermittelt und gespeichert:
SELECT table_catalog, table_schema, table_name, table_type FROM DEMO.INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA IN ('DEMO_SCHEMA')--, 'DEMO_SCHEMA_2') ORDER BY TABLE_SCHEMA, TABLE_NAME
Als nächstes wird über die Tabellen iteriert (IDX), die Daten selektiert und gespeichert:
SELECT * FROM [IDX:table_schema].[IDX:table_name]
Daten Import
Der Datenimport ist nicht ganz so einfach.
Eine Tabelle hat keinen Primärschlüssel und konnte nicht importiert werden. Da diese Tabelle auch keine Daten enthält, war das aber kein Problem und der Import dieser Tabelle konnte einfach ausgelassen werden. Später kann geprüft werden, ob diese Tabelle überhaupt noch verwendet wird oder final gelöscht werden kann.
Eine andere Tabelle hat eine Spalte mit der IDENTITY Eigenschaft und die Daten können nicht einfach so eingefügt werden, dazu muss zuerst das IDENTITY_INSERT für diese Tabelle eingeschaltet werden.
Allgemein besteht das Problem, dass die Tabellen über gewisse Constraints verfügen, die das naive importieren der Daten verhindern. Beispielsweise Foreign Keys, so dass die Daten in einer bestimmten Reihenfolge importiert werden müssten. Oder man deaktiviert für die Dauer des Imports alle Constraints und spart sich so die Sortiererei!
Das Import Script sieht ungefährt so aus:
-- disable all constraints EXEC sp_MSforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all" -- SET IDENTITY_INSERT demo_schema.mydemotable ON #IMPORT_CSV_FILES -- enable all constraints exec sp_MSforeachtable @command1="print '?'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"
Ein Problem mit Case Sensitiven Daten
Eine Tabelle bereitet mir noch Probleme:
In dieser Tabelle befinden sich Datensätze, deren Primärschlüssel sich lediglich in der Groß/Kleinschreibung unterscheiden, zB: "EinTollerDatensatz" und "eintollerdatensatz". In der alten DB waren das zwei unterschiedliche Schlüssel, in der neuen DB leider nicht und so können einige Datensätze nicht importiert werden.
Das Problem könnte mit der Collation, bzw. im Deutschen: Serversortierung, zusammenhängen. In der Servereigenschaften ist diese immer standardmäßig "SQL_LATIN1_General_CP1_CI_AS", wobei das "CI" für Case Insensitive steht. In den einzelnen Datenbanken des Servers kann man diese anpassen und eine Überprüfung der alten Datenbank ergab, dass diese "Latin1_General_CS_AS" ist. Daher habe ich der neuen Datenbank im Script nach dem CREATE DATABASE Befehl auch diese Eigenschaft zugewiesen:
CREATE DATABASE [DEMO] # [...] ALTER DATABASE [DEMO] COLLATE Latin1_General_CS_AS GO
Leider führte das zu weiteren, multiplen Fehlern. Daher habe ich mich an dieser Stelle erstmal dazu entschlossen, die Collation nicht zu ändern und mit fehlenden Datensätzen weiter zu arbeiten.
One reply on “MS-SQL DB in Docker Container”
[…] einer MS-SQL Datenbank zu exportieren und anschließend in die geDockerte Version zu kopieren (MS-SQL DB in Docker Container) habe ich die Chains verwendet, eine propritäre […]