Microsoft SQL Server - Conversion vers MySQL / PostgreSQL / SQLite
- 1. Vue d'ensemble et choix de l'outil
- 2. Equivalences de types de donnees
- 3. Differences de syntaxe SQL
- 4. MSSQL vers MySQL / MariaDB
- 5. MSSQL vers PostgreSQL (pgloader)
- 6. MSSQL vers SQLite
- 7. Methode universelle : CSV pivot
- 8. SSMA (SQL Server Migration Assistant)
- 9. Pieges encodage et collations
- 10. Checklist post-migration
1. Vue d'ensemble et choix de l'outil
Il n'existe pas de format universel entre SGBD : la conversion implique toujours une etape de transformation du SQL (types, syntaxe, fonctions). Trois grandes strategies :
| Strategie | Volume | Effort | Outils |
|---|---|---|---|
| Outil dedie | Petite a grosse | Faible | pgloader, SSMA, DBeaver, MySQL Workbench Migration Wizard |
| CSV pivot | Toutes | Moyen (DDL a refaire) | bcp → CSV → LOAD DATA / COPY |
| .sql + sed/regex | Petite | Eleve | mssql-scripter + script de transformation |
- → PostgreSQL : pgloader (meilleur ratio resultat / effort)
- → MySQL/MariaDB : MySQL Workbench Migration Wizard ou DBeaver
- → SQLite : conversion en 2 etapes via PostgreSQL ou via CSV
- → Migration enterprise : SSMA (gratuit Microsoft)
2. Equivalences de types de donnees
| MSSQL | MySQL / MariaDB | PostgreSQL | SQLite |
|---|---|---|---|
| TINYINT | TINYINT UNSIGNED | SMALLINT | INTEGER |
| SMALLINT | SMALLINT | SMALLINT | INTEGER |
| INT | INT | INTEGER | INTEGER |
| BIGINT | BIGINT | BIGINT | INTEGER |
| BIT | TINYINT(1) | BOOLEAN | INTEGER |
| DECIMAL(p,s) / NUMERIC | DECIMAL(p,s) | NUMERIC(p,s) | NUMERIC |
| MONEY | DECIMAL(19,4) | MONEY ou NUMERIC(19,4) | NUMERIC |
| FLOAT | DOUBLE | DOUBLE PRECISION | REAL |
| REAL | FLOAT | REAL | REAL |
| CHAR(n) | CHAR(n) | CHAR(n) | TEXT |
| VARCHAR(n) | VARCHAR(n) | VARCHAR(n) | TEXT |
| VARCHAR(MAX) | LONGTEXT | TEXT | TEXT |
| NCHAR / NVARCHAR | CHAR / VARCHAR + utf8mb4 | CHAR / VARCHAR (UTF-8 natif) | TEXT |
| NVARCHAR(MAX) | LONGTEXT CHARACTER SET utf8mb4 | TEXT | TEXT |
| TEXT / NTEXT | LONGTEXT | TEXT | TEXT |
| DATE | DATE | DATE | TEXT (ISO 8601) |
| TIME | TIME | TIME | TEXT |
| DATETIME | DATETIME | TIMESTAMP | TEXT |
| DATETIME2(n) | DATETIME(n) | TIMESTAMP(n) | TEXT |
| DATETIMEOFFSET | DATETIME + TZ separe | TIMESTAMPTZ | TEXT |
| SMALLDATETIME | DATETIME | TIMESTAMP(0) | TEXT |
| UNIQUEIDENTIFIER | CHAR(36) | UUID | TEXT |
| VARBINARY(n) | VARBINARY(n) | BYTEA | BLOB |
| VARBINARY(MAX) / IMAGE | LONGBLOB | BYTEA | BLOB |
| XML | LONGTEXT (ou XML MariaDB) | XML | TEXT |
| SQL_VARIANT | (non supporte) -> LONGTEXT | (non supporte) -> TEXT | TEXT |
| HIERARCHYID / GEOGRAPHY | VARBINARY / GEOMETRY | POSTGIS | (non supporte) |
3. Differences de syntaxe SQL
3.1 Auto-increment
-- MSSQL
Id INT IDENTITY(1,1) PRIMARY KEY
-- MySQL
Id INT AUTO_INCREMENT PRIMARY KEY
-- PostgreSQL (recommande : IDENTITY SQL standard depuis 10)
Id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
-- ou : Id SERIAL PRIMARY KEY
-- SQLite
Id INTEGER PRIMARY KEY AUTOINCREMENT
3.2 Identificateurs et chaines
-- MSSQL : crochets pour les identificateurs
SELECT [name] FROM [dbo].[Users]
-- MySQL : backticks
SELECT `name` FROM `Users`
-- PostgreSQL / SQLite : double-quotes (standard SQL)
SELECT "name" FROM "Users"
-- MSSQL : strings Unicode avec prefixe N
WHERE Nom = N'Frederic'
-- MySQL / PG / SQLite : pas de prefixe (utf-8 par defaut)
WHERE Nom = 'Frederic'
3.3 LIMIT / TOP / OFFSET
-- MSSQL
SELECT TOP 10 * FROM Clients
-- MSSQL avec offset (SQL 2012+)
SELECT * FROM Clients ORDER BY Id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
-- MySQL / PostgreSQL / SQLite
SELECT * FROM Clients LIMIT 10
SELECT * FROM Clients LIMIT 10 OFFSET 20
3.4 Concatenation
-- MSSQL
SELECT 'Bonjour ' + Nom -- + (NULL si l'un est NULL)
SELECT CONCAT('Bonjour ', Nom) -- ignore les NULL (2012+)
-- MySQL
SELECT CONCAT('Bonjour ', Nom)
-- Attention : + sert pour la multiplication numerique uniquement
-- PostgreSQL / SQLite
SELECT 'Bonjour ' || Nom
3.5 Date courante
-- MSSQL
SELECT GETDATE(), SYSUTCDATETIME(), CURRENT_TIMESTAMP
-- MySQL
SELECT NOW(), UTC_TIMESTAMP(), CURRENT_TIMESTAMP
-- PostgreSQL
SELECT NOW(), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP AT TIME ZONE 'UTC'
-- SQLite
SELECT datetime('now'), datetime('now', 'utc'), CURRENT_TIMESTAMP
3.6 Calculs de date
-- MSSQL
DATEADD(day, 7, GETDATE())
DATEDIFF(day, dt1, dt2)
DATEPART(year, dt)
-- MySQL
DATE_ADD(NOW(), INTERVAL 7 DAY)
DATEDIFF(dt2, dt1)
YEAR(dt)
-- PostgreSQL
NOW() + INTERVAL '7 days'
(dt2 - dt1) -- difference en jours
EXTRACT(YEAR FROM dt)
-- SQLite
date('now', '+7 days')
julianday(dt2) - julianday(dt1)
strftime('%Y', dt)
3.7 IF / CASE
-- Tous SGBD : CASE est portable
SELECT CASE WHEN Actif=1 THEN 'Oui' ELSE 'Non' END FROM Clients
-- MSSQL : IIF (raccourci)
SELECT IIF(Actif=1, 'Oui', 'Non') FROM Clients
-- MySQL : IF
SELECT IF(Actif=1, 'Oui', 'Non') FROM Clients
-- PostgreSQL : pas de IIF/IF, utiliser CASE
3.8 Booleen
-- MSSQL : BIT (0/1)
WHERE Actif = 1
-- MySQL : TINYINT(1) ou BOOLEAN (alias)
WHERE Actif = 1 -- ou WHERE Actif = TRUE
-- PostgreSQL : BOOLEAN vrai
WHERE Actif = TRUE
-- SQLite : INTEGER (0/1)
WHERE Actif = 1
3.9 Procedures et variables
-- MSSQL
DECLARE @id INT = 5;
SELECT * FROM Clients WHERE Id = @id;
-- MySQL
SET @id = 5;
SELECT * FROM Clients WHERE Id = @id;
-- PostgreSQL (en bloc DO ou fonction)
DO $$
DECLARE id INT := 5;
BEGIN
PERFORM * FROM Clients WHERE Id = id;
END $$;
4. MSSQL vers MySQL / MariaDB
4.1 MySQL Workbench Migration Wizard (recommande)
- Ouvrir MySQL Workbench → menu Database → Migration Wizard
- Source : Microsoft SQL Server + chaine de connexion (ODBC)
- Cible : MySQL / MariaDB
- L'outil convertit les types automatiquement (revoir les warnings)
- Generer et executer le script de migration des donnees
Prerequis : pilote ODBC Microsoft SQL Server installe sur le poste.
4.2 Methode manuelle : mssql-scripter + transformations
# 1. Export du schema en .sql depuis MSSQL
mssql-scripter -S localhost -d MaBase --integrated-auth --schema-only \
--target-server-version 2019 \
-f mssql_schema.sql
# 2. Export des donnees (INSERT)
mssql-scripter -S localhost -d MaBase --integrated-auth --data-only \
-f mssql_data.sql
# 3. Transformer le fichier (PowerShell)
Script PowerShell de conversion basique :
$content = Get-Content -Raw -Path "mssql_schema.sql"
# Crochets -> backticks
$content = $content -replace '\[(\w+)\]', '`$1`'
# IDENTITY -> AUTO_INCREMENT
$content = $content -replace 'IDENTITY\(\s*1\s*,\s*1\s*\)', 'AUTO_INCREMENT'
# NVARCHAR -> VARCHAR
$content = $content -replace 'NVARCHAR', 'VARCHAR'
# DATETIME2 -> DATETIME
$content = $content -replace 'DATETIME2(\(\d+\))?', 'DATETIME'
# BIT -> TINYINT(1)
$content = $content -replace '\bBIT\b', 'TINYINT(1)'
# UNIQUEIDENTIFIER -> CHAR(36)
$content = $content -replace 'UNIQUEIDENTIFIER', 'CHAR(36)'
# Supprimer les GO et les WITH/ON/PAD_INDEX...
$content = $content -replace '(?m)^GO\s*$', ';'
$content = $content -replace 'WITH \([^)]*\)', ''
$content = $content -replace ' ON \[PRIMARY\]', ''
$content = $content -replace 'dbo\.', ''
$content = $content -replace 'N''', '''' # N'foo' -> 'foo'
$content | Set-Content -Path "mysql_schema.sql" -Encoding UTF8
4.3 Import dans MySQL
# Creer la base MySQL
mysql -u root -p -e "CREATE DATABASE ma_base CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# Importer le schema
mysql -u root -p ma_base < mysql_schema.sql
# Importer les donnees
mysql -u root -p ma_base < mssql_data.sql
4.4 Via CSV (table par table, le plus fiable)
# 1. Export bcp depuis MSSQL en UTF-8
bcp MaBase.dbo.Clients out clients.csv -c -t"|" -C 65001 -S localhost -T
# 2. Creer la table MySQL manuellement
mysql -u root -p ma_base -e "
CREATE TABLE Clients (
Id INT PRIMARY KEY,
Nom VARCHAR(100),
Email VARCHAR(200)
) DEFAULT CHARSET=utf8mb4;
"
# 3. Importer via LOAD DATA
mysql -u root -p ma_base -e "
LOAD DATA LOCAL INFILE 'clients.csv'
INTO TABLE Clients
FIELDS TERMINATED BY '|'
LINES TERMINATED BY '\n'
(Id, Nom, Email);
"
5. MSSQL vers PostgreSQL (pgloader)
pgloader est l'outil le plus mature pour migrer MSSQL vers PostgreSQL : conversion des types, donnees, contraintes, sequences IDENTITY → SERIAL/IDENTITY.
5.1 Installation
# Ubuntu / Debian
sudo apt install pgloader
# Docker (le plus simple sur Windows)
docker pull dimitri/pgloader
5.2 Migration en une commande
pgloader \
"mssql://user:password@srv-mssql/MaBase" \
"postgresql://postgres:password@srv-pg/ma_base"
5.3 Avec fichier de configuration (recommande)
Creer un fichier migration.load :
LOAD DATABASE
FROM mssql://user:password@srv-mssql/MaBase
INTO postgresql://postgres:password@srv-pg/ma_base
WITH
include drop, -- supprime les tables existantes en cible
create tables, -- cree le schema
create indexes,
reset sequences,
data only, -- (a inverser : "schema only" pour ne pas migrer les donnees)
workers = 8,
concurrency = 4
CAST
type bit to boolean drop typemod,
type datetime to timestamp drop typemod,
type datetime2 to timestamp drop typemod,
type uniqueidentifier to uuid,
type nvarchar to text,
type ntext to text,
type varbinary to bytea drop typemod
ALTER SCHEMA 'dbo' RENAME TO 'public'
BEFORE LOAD DO
$$ CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; $$;
Lancer :
pgloader migration.load
5.4 Avec Docker
docker run --rm --network host \
-v ${PWD}:/data \
dimitri/pgloader \
pgloader /data/migration.load
.log pour audit.
5.5 Conversion manuelle (CSV)
# Export MSSQL -> CSV (utf-8, separateur tab)
bcp MaBase.dbo.Clients out clients.tsv -c -t"`t" -C 65001 -S localhost -T
# Import PostgreSQL
psql -U postgres -d ma_base -c "\COPY Clients FROM 'clients.tsv' WITH (FORMAT csv, DELIMITER E'\t', HEADER false, ENCODING 'UTF8');"
6. MSSQL vers SQLite
SQLite a des contraintes specifiques : pas de procedures, pas de types stricts, pas de droits utilisateurs. La migration est souvent unidirectionnelle (lecture seule ou usage embarque).
6.1 Strategie recommandee : MSSQL → PostgreSQL → SQLite
Plus fiable car pgloader gere mieux MSSQL et pgloader + pg2sqlite font le second saut.
6.2 Strategie directe via CSV + script Python
import csv, sqlite3, pyodbc
# 1. Connexion MSSQL
cnx_src = pyodbc.connect(
'DRIVER={ODBC Driver 17 for SQL Server};'
'SERVER=localhost;DATABASE=MaBase;Trusted_Connection=yes;'
)
# 2. Connexion SQLite
cnx_dst = sqlite3.connect('ma_base.db')
cur_dst = cnx_dst.cursor()
# 3. Pour chaque table : lire MSSQL + ecrire SQLite
cur_src = cnx_src.cursor()
cur_src.execute("SELECT name FROM sys.tables")
tables = [r[0] for r in cur_src.fetchall()]
for table in tables:
# Recuperer la structure
cur_src.execute(f"SELECT TOP 0 * FROM [{table}]")
cols = [c[0] for c in cur_src.description]
cols_def = ', '.join(f'"{c}"' for c in cols)
cur_dst.execute(f'DROP TABLE IF EXISTS "{table}"')
cur_dst.execute(f'CREATE TABLE "{table}" ({cols_def})')
# Copier les donnees par batch
cur_src.execute(f"SELECT * FROM [{table}]")
placeholders = ','.join(['?'] * len(cols))
while True:
rows = cur_src.fetchmany(1000)
if not rows:
break
cur_dst.executemany(
f'INSERT INTO "{table}" VALUES ({placeholders})',
rows
)
cnx_dst.commit()
print(f" {table} OK")
cnx_dst.close()
cnx_src.close()
6.3 Outil graphique : DBeaver
- Connecter MSSQL et SQLite dans DBeaver
- Clic droit sur la base SQLite → Tools → Data Migration
- Selectionner les tables MSSQL source
- Mapper les colonnes (correction des types si besoin)
- Lancer la migration
7. Methode universelle : CSV pivot
Quand aucun outil n'est disponible, le format CSV reste l'echange le plus interoperable. Procedure type :
7.1 Etape 1 : Export du DDL MSSQL (a adapter)
mssql-scripter -S localhost -d MaBase --integrated-auth --schema-only -f schema.sql
7.2 Etape 2 : Reecriture du DDL pour la cible
Reprendre le DDL et ajuster selon le tableau d'equivalence (section 2).
7.3 Etape 3 : Export des donnees en CSV (UTF-8)
# Script PowerShell : exporter toutes les tables d'un schema en CSV
$server = "localhost"
$base = "MaBase"
$dest = "D:\migration\csv"
New-Item -ItemType Directory -Force -Path $dest | Out-Null
# Lister les tables
$tablesQuery = @"
SET NOCOUNT ON;
SELECT s.name + '.' + t.name
FROM sys.tables t
JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE s.name = 'dbo'
"@
$tables = & sqlcmd -S $server -d $base -E -h-1 -W -Q $tablesQuery
foreach ($t in $tables) {
$t = $t.Trim()
if ($t -and $t -notmatch "rows affected") {
$shortName = ($t -replace '\.','_')
$out = Join-Path $dest "$shortName.csv"
Write-Host "Export $t -> $out"
# Export avec separateur |, UTF-8, sans en-tete
& bcp "$base.$t" out $out -c -t"|" -r"\n" -C 65001 -S $server -T
}
}
7.4 Etape 4 : Import dans le SGBD cible
# MySQL
LOAD DATA LOCAL INFILE 'clients.csv' INTO TABLE Clients
FIELDS TERMINATED BY '|' LINES TERMINATED BY '\n' CHARACTER SET utf8mb4;
# PostgreSQL
\COPY Clients FROM 'clients.csv' WITH (FORMAT csv, DELIMITER '|', ENCODING 'UTF8');
# SQLite (CLI)
sqlite3 ma_base.db ".mode csv" ".separator |" ".import clients.csv Clients"
| ou \x1F) et nettoyer en amont si necessaire.
8. SSMA (SQL Server Migration Assistant)
Outil Microsoft gratuit, mais utilisable dans le sens inverse de l'habituel : SSMA migre vers MSSQL depuis Oracle, MySQL, Access, etc. Pour migrer MSSQL → MySQL/PostgreSQL, SSMA n'est pas utilisable.
SSMA est utile dans 3 cas :
- Migration depuis MySQL/Access/Oracle vers MSSQL
- Migration MSSQL on-premise vers Azure SQL
- Conversion de procedures stockees
Telechargement : SQL Server Migration Assistant sur le site Microsoft Learn.
8.1 Alternative inverse : Azure Database Migration Service (DMS)
Service Cloud Azure pour migrer MSSQL on-prem vers Azure SQL ou inversement. Payant a l'usage.
9. Pieges encodage et collations
9.1 Encodage par defaut MSSQL
| Type MSSQL | Encodage stocke |
|---|---|
| VARCHAR | Code page de la collation (ex: Windows-1252 en France) |
| NVARCHAR / NCHAR / NTEXT | UTF-16 LE |
| VARCHAR + collation UTF8 (MSSQL 2019+) | UTF-8 |
VARCHAR contenant des accents stockes en Windows-1252 vers MySQL en UTF-8 produit des caracteres mal encodes. Toujours :
- Forcer l'encodage de sortie avec
bcp -C 65001 - Verifier en lisant le CSV en mode binaire (hexa) ou avec Notepad++ (statut UTF-8)
- Cote MySQL : creer la base avec
CHARACTER SET utf8mb4
9.2 Convertir un dump entre encodages
# PowerShell : convertir Windows-1252 vers UTF-8
Get-Content -Path dump.sql -Encoding Default |
Out-File -FilePath dump_utf8.sql -Encoding UTF8
# Linux / Git Bash : iconv
iconv -f WINDOWS-1252 -t UTF-8 dump.sql > dump_utf8.sql
9.3 Collations comparees
| MSSQL | MySQL | PostgreSQL |
|---|---|---|
| French_CI_AS | utf8mb4_unicode_ci | fr_FR.UTF-8 (LC_COLLATE) |
| Latin1_General_CI_AS | utf8mb4_general_ci | C ou en_US.UTF-8 |
| French_BIN2 | utf8mb4_bin | C |
CI = Case Insensitive, AS = Accent Sensitive. Choisir la collation cible pour preserver le comportement des recherches (LIKE, ORDER BY).
10. Checklist post-migration
10.1 Verifications de coherence
-- Cote source MSSQL
SELECT name, (SELECT COUNT(*) FROM Clients) FROM sys.tables;
-- Cote cible (a comparer)
-- MySQL :
SELECT TABLE_NAME, TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='ma_base';
-- PostgreSQL :
SELECT relname, n_live_tup FROM pg_stat_user_tables;
10.2 Comparer les comptages exacts (sample query)
# Script PowerShell : compare le nb de lignes entre MSSQL et MySQL
$mssqlCount = & sqlcmd -S srv-mssql -E -d MaBase -h-1 -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM dbo.Clients"
$mysqlCount = mysql -h srv-mysql -u user -ppass ma_base -se "SELECT COUNT(*) FROM Clients"
if ($mssqlCount.Trim() -eq $mysqlCount.Trim()) {
Write-Host "OK : $($mssqlCount.Trim()) lignes des deux cotes" -ForegroundColor Green
} else {
Write-Host "DIVERGENCE : MSSQL=$mssqlCount MySQL=$mysqlCount" -ForegroundColor Red
}
10.3 A reverifier manuellement
- Procedures stockees / fonctions / triggers (non migres par la plupart des outils)
- Vues : verifier la syntaxe (TOP, IIF, +concat)
- Index manquants ou renommes
- Contraintes
CHECKavec syntaxe MSSQL specifique - Sequences / IDENTITY : valeur de depart apres reseed (
setvalen PG,AUTO_INCREMENT=en MySQL) - Donnees binaires (images, fichiers) : verifier qu'aucune troncation
- Dates < 1753-01-01 (limite MSSQL datetime) : converties en NULL ou date plancher cote source
- Caracteres speciaux (NULL bytes, controls) :
bcppeut les exporter mais MySQL/PG les refuser
10.4 Tests applicatifs
- Connecter l'application sur la BDD cible (string de connexion + driver)
- Tester un parcours complet : lecture, ecriture, recherche avec accents, tri
- Lancer une charge representative (lecture/ecriture concurrentes)
- Comparer les temps de reponse a la base source