🛠 Outils IT
🛠 98 outils 📚 37 docs
🤖 Assistant

Microsoft SQL Server - Conversion vers MySQL / PostgreSQL / SQLite

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 :

StrategieVolumeEffortOutils
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
Recommandation par cible :
  • → 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

MSSQLMySQL / MariaDBPostgreSQLSQLite
TINYINTTINYINT UNSIGNEDSMALLINTINTEGER
SMALLINTSMALLINTSMALLINTINTEGER
INTINTINTEGERINTEGER
BIGINTBIGINTBIGINTINTEGER
BITTINYINT(1)BOOLEANINTEGER
DECIMAL(p,s) / NUMERICDECIMAL(p,s)NUMERIC(p,s)NUMERIC
MONEYDECIMAL(19,4)MONEY ou NUMERIC(19,4)NUMERIC
FLOATDOUBLEDOUBLE PRECISIONREAL
REALFLOATREALREAL
CHAR(n)CHAR(n)CHAR(n)TEXT
VARCHAR(n)VARCHAR(n)VARCHAR(n)TEXT
VARCHAR(MAX)LONGTEXTTEXTTEXT
NCHAR / NVARCHARCHAR / VARCHAR + utf8mb4CHAR / VARCHAR (UTF-8 natif)TEXT
NVARCHAR(MAX)LONGTEXT CHARACTER SET utf8mb4TEXTTEXT
TEXT / NTEXTLONGTEXTTEXTTEXT
DATEDATEDATETEXT (ISO 8601)
TIMETIMETIMETEXT
DATETIMEDATETIMETIMESTAMPTEXT
DATETIME2(n)DATETIME(n)TIMESTAMP(n)TEXT
DATETIMEOFFSETDATETIME + TZ separeTIMESTAMPTZTEXT
SMALLDATETIMEDATETIMETIMESTAMP(0)TEXT
UNIQUEIDENTIFIERCHAR(36)UUIDTEXT
VARBINARY(n)VARBINARY(n)BYTEABLOB
VARBINARY(MAX) / IMAGELONGBLOBBYTEABLOB
XMLLONGTEXT (ou XML MariaDB)XMLTEXT
SQL_VARIANT(non supporte) -> LONGTEXT(non supporte) -> TEXTTEXT
HIERARCHYID / GEOGRAPHYVARBINARY / GEOMETRYPOSTGIS(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)

  1. Ouvrir MySQL Workbench → menu DatabaseMigration Wizard
  2. Source : Microsoft SQL Server + chaine de connexion (ODBC)
  3. Cible : MySQL / MariaDB
  4. L'outil convertit les types automatiquement (revoir les warnings)
  5. 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
Limite : ce script ne traite que les cas simples. Pour les procedures stockees, fonctions, triggers, vues : reecriture manuelle obligatoire.

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
Logs : pgloader produit un rapport detaille (lignes migrees, erreurs, durees). Conserver le fichier .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

  1. Connecter MSSQL et SQLite dans DBeaver
  2. Clic droit sur la base SQLite → ToolsData Migration
  3. Selectionner les tables MSSQL source
  4. Mapper les colonnes (correction des types si besoin)
  5. 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"
Echappement : bcp ne gere PAS l'echappement des sauts de ligne ou des separateurs presents dans les donnees. Choisir un separateur improbable dans les donnees (ex: | 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 MSSQLEncodage stocke
VARCHARCode page de la collation (ex: Windows-1252 en France)
NVARCHAR / NCHAR / NTEXTUTF-16 LE
VARCHAR + collation UTF8 (MSSQL 2019+)UTF-8
Piege classique : exporter une table avec VARCHAR contenant des accents stockes en Windows-1252 vers MySQL en UTF-8 produit des caracteres mal encodes. Toujours :
  1. Forcer l'encodage de sortie avec bcp -C 65001
  2. Verifier en lisant le CSV en mode binaire (hexa) ou avec Notepad++ (statut UTF-8)
  3. 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

MSSQLMySQLPostgreSQL
French_CI_ASutf8mb4_unicode_cifr_FR.UTF-8 (LC_COLLATE)
Latin1_General_CI_ASutf8mb4_general_ciC ou en_US.UTF-8
French_BIN2utf8mb4_binC

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 CHECK avec syntaxe MSSQL specifique
  • Sequences / IDENTITY : valeur de depart apres reseed (setval en 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) : bcp peut 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
Voir aussi : documentation MSSQL - Export / Dump pour generer les sources, et MSSQL - Import / Restauration pour restaurer cote MSSQL.