Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
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. |