Migrer vers go-mssqldb depuis d’autres pilotes

Ce guide aide les développeurs Go à migrer de PostgreSQL, MySQL et d’autres pilotes de bases de données vers go-mssqldb SQL Server. Il couvre les différences de syntaxe SQL, les modifications spécifiques au pilote et les motifs équivalents pour les opérations courantes.

Note

Les exemples de cet article s’exécutent sur la base de données d’exemple AdventureWorks2025.

Pourquoi migrer vers SQL Server avec go-mssqldb

Fonctionnalité SQL Server avec go-mssqldb
Sécurité d’entreprise Toujours chiffré, sécurité au niveau des lignes, masquage dynamique des données, chiffrement transparent des données (TDE).
Authentication Microsoft Entra ID (anciennement Azure AD), identité gérée, Authentification Windows, Kerberos.
Intégration Azure Prise en charge native d’Azure SQL Database avec chiffrement automatique, connexions sans mot de passe et gestion du basculement.
Performance Index de colonnal, OLTP en mémoire, magasin de requêtes pour correction automatique de plans.
Formats de données Prise en charge JSON et XML intégrée avec indexation et requête côté serveur.
Outillage SQL Server Management Studio (SSMS), Azure Data Studio, extensions SQL de Visual Studio Code.

Liste de contrôle des considérations de migration

Examinez ces éléments avant de commencer à porter les requêtes ligne par ligne :

Considérations Nouveautés
La syntaxe des paramètres fictifs diffère de celle de nombreux pilotes Go. Remplacez ?, $1, et autres placeholders similaires par @name ou @p1, puis passez sql.Named() les arguments lorsque cela est approprié.
LastInsertId() n’est pas pris en charge. Utilisez plutôt OUTPUT INSERTED.<column> ou SELECT SCOPE_IDENTITY().
Les valeurs Go string correspondent par défaut aux types Unicode. Attendez-vous à la sémantique de nvarchar, sauf si vous choisissez un type spécifique au pilote, comme mssql.VarChar pour les données non Unicode.
Les tables temporaires sont limitées à la connexion physique. Gardez la logique de création et d’utilisation de la table temporaire sur la même connexion ou à l’intérieur de la même transaction si les instructions ultérieures dépendent de cette table.
L’authentification Microsoft Entra utilise un autre enregistrement de pilote. Importez github.com/microsoft/go-mssqldb/azuread, utilisez sql.Open("azuresql", ...), et définissez explicitement les options TLS Azure SQL avec encrypt=true&TrustServerCertificate=false.

Pour des conseils plus approfondis sur ces domaines, voir Requêtes et instructions, Limitations, Mappage de types de données, Authentification Microsoft Entra ID et Dépannage.

Migrer depuis lib/pq (PostgreSQL)

Changer l’importation et le nom du pilote

Remplacez l’instruction d’importation lib/pq et le nom du pilote par go-mssqldb :

import _ "github.com/lib/pq"
db, err := sql.Open("postgres", connString)

// After (SQL Server with go-mssqldb):
import _ "github.com/microsoft/go-mssqldb"
db, err := sql.Open("sqlserver", connString)

Changer le format de la chaîne de connexion

Convertir le format clé-valeur PostgreSQL en une URL SQL Server :

// lib/pq connection string:
"host=<server> port=5432 user=<user> password=<password> dbname=AdventureWorks2025 sslmode=require"

// go-mssqldb connection string:
"sqlserver://<user>:<password>@<server>:1433?database=AdventureWorks2025&encrypt=true"

Modifier les espaces réservés des paramètres

PostgreSQL utilise $1, $2 comme paramètres fictifs positionnels. SQL Server utilise des paramètres nommés avec @:

// PostgreSQL (lib/pq):
db.QueryContext(ctx, "SELECT * FROM users WHERE id = $1 AND status = $2", id, status)

// SQL Server (go-mssqldb):
db.QueryContext(ctx, "SELECT ProductID, Name FROM Production.Product WHERE ProductID = @id AND Color = @color",
    sql.Named("id", id),
    sql.Named("color", color))

Modifications de la syntaxe SQL

Operation PostgreSQL SQL Server
Incrément automatique SERIAL ou GENERATED ALWAYS AS IDENTITY IDENTITY(1,1)
Faites entrer votre identifiant RETURNING id OUTPUT INSERTED.id ou SELECT SCOPE_IDENTITY()
Type booléen BOOLEAN BIT
Concaténation de chaînes \|\| + ou CONCAT()
Horodatage actuel NOW() ou CURRENT_TIMESTAMP GETUTCDATE() ou SYSDATETIMEOFFSET()
Limiter les lignes LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Type de chaîne TEXT ou VARCHAR NVARCHAR(MAX) ou NVARCHAR(n)
Extraction de JSON column->>'key' JSON_VALUE(column, '$.key')
Upsert INSERT ... ON CONFLICT DO UPDATE Instruction MERGE
Array, type INTEGER[] Pas de réseaux natifs. Utilisez des paramètres à valeurs de table.
Respect de la casse Sensible à la casse par défaut Insensible à la casse par défaut (cela dépend de la collation).

Obtenez l’ID inséré

Remplacez la clause de RETURNING PostgreSQL par celle de OUTPUT SQL Server :

var id int
err := db.QueryRowContext(ctx,
    "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id",
    name, email).Scan(&id)

// SQL Server (go-mssqldb) using OUTPUT:
var id int
err := db.QueryRowContext(ctx,
    "INSERT INTO HumanResources.Department (Name, GroupName) OUTPUT INSERTED.DepartmentID VALUES (@name, @groupName)",
    sql.Named("name", name),
    sql.Named("groupName", groupName)).Scan(&id)

Pagination

Remplacez la syntaxe LIMIT/OFFSET par la syntaxe OFFSET/FETCH NEXT.

// PostgreSQL:
"SELECT * FROM employees ORDER BY id LIMIT $1 OFFSET $2"

// SQL Server (requires ORDER BY):
"SELECT ProductID, Name, ListPrice FROM Production.Product ORDER BY ProductID OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY"

Migrer depuis pgx (PostgreSQL)

Le pgx pilote utilise son propre pool de connexion et API qui diffèrent de database/sql. Pour migrer, passez à l’interface standard database/sql .

Passage du pool pgx à base de données/sql

Remplacez l’API pgxpool par l’interface standard database/sql .

// Before (pgx):
import "github.com/jackc/pgx/v5/pgxpool"
pool, err := pgxpool.New(ctx, "postgres://user:pass@<server>:5432/AdventureWorks2025")
defer pool.Close()
rows, err := pool.Query(ctx, "SELECT * FROM users WHERE id = $1", id)

// After (go-mssqldb with database/sql):
import (
    "database/sql"
    _ "github.com/microsoft/go-mssqldb"
)
db, err := sql.Open("sqlserver", "sqlserver://user:pass@<server>:1433?database=AdventureWorks2025")
defer db.Close()
rows, err := db.QueryContext(ctx,
    "SELECT ProductID, Name FROM Production.Product WHERE ProductID = @id", sql.Named("id", id))

Remplacer les fonctionnalités spécifiques aux pgx

fonctionnalité pgx Équivalent de go-mssqldb
pgx.CollectRows Boucle manuelle rows.Next() et rows.Scan().
pgx.RowToStructByName Saisie manuelle de rows.Scan dans les champs de structure.
Requêtes par lots (pgx.Batch) Appels multiples ExecContext ou procédures stockées.
COPY FROM pour l’insertion en vrac mssql.CopyIn pour l’insertion en bloc.
pgx.ConnConfig msdsn.Config ou des chaînes de connexion basées sur des URL.
pgxpool.Pool sql.DB avec SetMaxOpenConns et SetMaxIdleConns.

Migrer depuis go-sql-driver/mysql (MySQL)

Permuter l’import MySQL et le nom du pilote

Remplacez l’import MySQL et le nom du pilote par go-mssqldb.

// Before (MySQL):
import _ "github.com/go-sql-driver/mysql"
db, err := sql.Open("mysql", connString)

// After (SQL Server):
import _ "github.com/microsoft/go-mssqldb"
db, err := sql.Open("sqlserver", connString)

Convertir le format MySQL DSN

Convertissez le format MySQL DSN en une URL SQL Server.

// MySQL DSN:
"user:password@tcp(<server>:3306)/AdventureWorks2025?tls=true"

// go-mssqldb URL:
"sqlserver://user:password@<server>:1433?database=AdventureWorks2025&encrypt=true"

Remplacez les espaces réservés en forme de point d’interrogation

MySQL utilise des paramètres fictifs positionnels ?.

// MySQL:
db.QueryContext(ctx, "SELECT * FROM users WHERE id = ? AND status = ?", id, status)

// SQL Server:
db.QueryContext(ctx, "SELECT ProductID, Name FROM Production.Product WHERE ProductID = @id AND Color = @color",
    sql.Named("id", id),
    sql.Named("color", color))

Comparez la syntaxe de MySQL et SQL Server

Operation MySQL SQL Server
Incrément automatique AUTO_INCREMENT IDENTITY(1,1)
Faites entrer votre identifiant LAST_INSERT_ID() SCOPE_IDENTITY() ou OUTPUT INSERTED.id
Limiter les lignes LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Horodatage actuel NOW() GETUTCDATE()
Si nul IFNULL(expr, default) ISNULL(expr, default) ou COALESCE(expr, default)
Longueur de chaîne LENGTH(str) LEN(str)
Sous-chaîne SUBSTRING(str, start, len) SUBSTRING(str, start, len) (Identique)
Format de date DATE_FORMAT(d, '%Y-%m-%d') FORMAT(d, 'yyyy-MM-dd') ou CONVERT(VARCHAR, d, 23)
Upsert INSERT ... ON DUPLICATE KEY UPDATE Instruction MERGE
Citation de backticks `column` [column]

Remplacer la syntaxe spécifique à MySQL

Syntaxe MySQL Équivalent de SQL Server
AUTO_INCREMENT IDENTITY(1,1)
LIMIT n OFFSET m OFFSET m ROWS FETCH NEXT n ROWS ONLY (nécessite ORDER BY)

AUTO_INCREMENT jusqu’à IDENTITY:

-- MySQL:
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100)
);

-- SQL Server:
CREATE TABLE HumanResources.NewDepartment (
    DepartmentID SMALLINT IDENTITY(1,1) PRIMARY KEY,
    Name NVARCHAR(50) NOT NULL
);

LIMIT par rapport à OFFSET/FETCH :

-- MySQL:
SELECT * FROM users LIMIT 10 OFFSET 20;

-- SQL Server:
SELECT ProductID, Name, ListPrice
FROM Production.Product
ORDER BY ProductID
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

Modèles de migration courants

Obtenez la dernière identité insérée

Utilisez la OUTPUT clause ou SCOPE_IDENTITY() pour récupérer la valeur d’identité de la ligne insérée :

// Pattern 1: OUTPUT clause (recommended, works with batch inserts).
var id int64
err := db.QueryRowContext(ctx,
    "INSERT INTO HumanResources.Department (Name, GroupName) OUTPUT INSERTED.DepartmentID VALUES (@name, @groupName)",
    sql.Named("name", "Engineering"),
    sql.Named("groupName", "Research and Development")).Scan(&id)

// Pattern 2: SCOPE_IDENTITY (works with single-row inserts).
var id int64
err := db.QueryRowContext(ctx,
    "INSERT INTO HumanResources.Department (Name, GroupName) VALUES (@name, @groupName); SELECT SCOPE_IDENTITY()",
    sql.Named("name", "Engineering"),
    sql.Named("groupName", "Research and Development")).Scan(&id)

Tip

Préfèrent la OUTPUT clause à SCOPE_IDENTITY(). La OUTPUT clause fonctionne avec les inserts par lots et ne dépend pas de l’ordre des instructions.

Upsert (insérer ou mettre à jour)

ON CONFLICT de PostgreSQL et ON DUPLICATE KEY UPDATE de MySQL correspondent à MERGE de SQL Server :

_, err := db.ExecContext(ctx, `
    MERGE HumanResources.Department AS target
    USING (SELECT @id AS DepartmentID, @name AS Name, @groupName AS GroupName) AS source
    ON target.DepartmentID = source.DepartmentID
    WHEN MATCHED THEN
        UPDATE SET Name = source.Name, GroupName = source.GroupName, ModifiedDate = GETDATE()
    WHEN NOT MATCHED THEN
        INSERT (Name, GroupName, ModifiedDate) VALUES (source.Name, source.GroupName, GETDATE());`,
    sql.Named("id", dept.ID),
    sql.Named("name", dept.Name),
    sql.Named("groupName", dept.GroupName))

Insertion en bloc

Remplacez COPY de PostgreSQL ou LOAD DATA de MySQL par mssql.CopyIn.

import mssql "github.com/microsoft/go-mssqldb"

stmt, err := db.Prepare(mssql.CopyIn("HumanResources.Department", mssql.BulkOptions{}, "Name", "GroupName", "ModifiedDate"))
if err != nil {
    return err
}

for _, dept := range departments {
    _, err = stmt.Exec(dept.Name, dept.GroupName, time.Now())
    if err != nil {
        return err
    }
}

// Flush the buffer.
_, err = stmt.Exec()
if err != nil {
    return err
}
stmt.Close()

Gérer les valeurs NULL

Tous les pilotes de base de données Go gèrent les valeurs NULL de la même manière car ils utilisent les database/sql types.

var color sql.NullString
err := db.QueryRowContext(ctx,
    "SELECT Color FROM Production.Product WHERE ProductID = @id",
    sql.Named("id", 1)).Scan(&color)

if color.Valid {
    fmt.Println(color.String)
} else {
    fmt.Println("NULL")
}

Transactions

Tous les pilotes de base de données Go utilisent la même database/sql API de transaction.

tx, err := db.BeginTx(ctx, nil)
if err != nil {
    return err
}
defer tx.Rollback()

_, err = tx.ExecContext(ctx,
    "UPDATE Production.ProductInventory SET Quantity = Quantity - @qty WHERE ProductID = @pid AND LocationID = @fromLoc",
    sql.Named("qty", qty),
    sql.Named("pid", productID),
    sql.Named("fromLoc", fromLocationID))
if err != nil {
    return err
}

_, err = tx.ExecContext(ctx,
    "UPDATE Production.ProductInventory SET Quantity = Quantity + @qty WHERE ProductID = @pid AND LocationID = @toLoc",
    sql.Named("qty", qty),
    sql.Named("pid", productID),
    sql.Named("toLoc", toLocationID))
if err != nil {
    return err
}

return tx.Commit()

Liste des éléments à vérifier pour la migration

Étape Action
Installer le pilote go get github.com/microsoft/go-mssqldb
Modifier les importations Remplacez l’importation de l’ancien pilote par _ "github.com/microsoft/go-mssqldb".
Mettre à jour les chaînes de connexion Changement vers le format URL de SQL Server : sqlserver://user:pass@host?database=db.
Remplacez les paramètres réservés Changer $1/$2 ou ? avec .@namesql.Named
Mettre à jour la syntaxe SQL Modifier LIMIT/OFFSET, RETURNING, NOW(), SERIAL, et autres SQL spécifiques à la base de données.
Remplacement des opérations en vrac Changer COPY ou LOAD DATA à mssql.CopyIn.
Mise à jour du schéma DDL Changer SERIAL/AUTO_INCREMENT en IDENTITY, TEXT à NVARCHAR, BOOLEAN en .BIT
Tester toutes les requêtes Faites tourner votre suite de tests contre une instance de SQL Server pour détecter les différences de syntaxe.
Configurer le chiffrement Ajouter encrypt=true (ou s’appuyer sur la détection automatique d’Azure) pour la production.
Configurer l’authentification Configurez Microsoft Entra ID pour Azure, ou l’authentification SQL Server pour les sites locaux.