Migrar a go-mssqldb desde otros controladores

Esta guía ayuda a los desarrolladores de Go a migrar desde PostgreSQL, MySQL y otros controladores de bases de datos a go-mssqldb para SQL Server. Cubre las diferencias de sintaxis de SQL, los cambios específicos de cada controlador y los patrones equivalentes para las operaciones comunes.

Nota:

Los ejemplos de este artículo se aplican a la base de datos de ejemplo AdventureWorks2025 .

Por qué migrar a SQL Server con go-mssqldb

Feature SQL Server con go-mssqldb
Seguridad empresarial Siempre cifrado, seguridad a nivel de fila, enmascaramiento dinámico de datos, cifrado transparente de datos (TDE).
Authentication Microsoft Entra ID (anteriormente Azure AD), identidad gestionada, autenticación de Windows, Kerberos.
Integración de Azure Soporte nativo de Azure SQL Database con cifrado automático, conexiones sin contraseña y gestión de conmutación por error.
Performance Índices de almacén de columnas, OLTP en memoria, almacén de consultas para la corrección automática de planes.
Formatos de datos Soporte integrado para JSON y XML con indexación y consulta en el lado del servidor.
Tooling SQL Server Management Studio (SSMS), Azure Data Studio, las extensiones SQL de Visual Studio Code.

Lista de comprobación sobre consideraciones migratorias

Revisa estos elementos antes de empezar a portar consultas línea por línea:

Consideración Qué cambiar
La sintaxis de los marcadores de posición difiere de la de muchos drivers de Go. Sustituye ?, $1, y otros marcadores similares por @name o @p1, y luego pasa sql.Named() los argumentos cuando sea apropiado.
LastInsertId() no es compatible. Use OUTPUT INSERTED.<column> o SELECT SCOPE_IDENTITY() en su lugar.
Los valores de Go string se asignan por defecto a los tipos Unicode. Se presupone la semántica de nvarchar, a menos que elijas un tipo específico del controlador, como mssql.VarChar, para datos no Unicode.
Las tablas temporales tienen como ámbito la conexión física. Mantén la lógica de creación y uso de la tabla temporal en la misma conexión o dentro de la misma transacción si las sentencias posteriores dependen de esa tabla.
La autenticación de Microsoft Entra utiliza un registro de controlador diferente. Importa github.com/microsoft/go-mssqldb/azuread, usa sql.Open("azuresql", ...), y establece explícitamente las opciones de Azure SQL TLS con encrypt=true&TrustServerCertificate=false.

Para una orientación más profunda sobre estas áreas, consulte Consultas y sentencias, Limitaciones, Mapeos de tipos de datos, Autenticación de Microsoft Entra ID y Resolución de problemas.

Migrar desde lib/pq (PostgreSQL)

Cambiar el nombre de importación y del piloto

Sustituye la instrucción de importación lib/pq y el nombre del controlador por 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)

Cambiar el formato de la cadena de conexión

Convierte el formato clave-valor de PostgreSQL a una URL de 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"

Cambiar los marcadores de posición de los parámetros

PostgreSQL utiliza $1, $2 marcadores posicionales. SQL Server utiliza parámetros con nombre con @:

// 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))

Cambios en la sintaxis de SQL

Operation PostgreSQL SQL Server
Incremento automático SERIAL o GENERATED ALWAYS AS IDENTITY IDENTITY(1,1)
Inserta el DNI RETURNING id OUTPUT INSERTED.id o SELECT SCOPE_IDENTITY()
Tipo booleano BOOLEAN BIT
Concatenación de cadenas \|\| + o CONCAT()
Marca de tiempo actual NOW() o CURRENT_TIMESTAMP GETUTCDATE() o SYSDATETIMEOFFSET()
Limitar filas LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Tipo de cadena TEXT o VARCHAR NVARCHAR(MAX) o NVARCHAR(n)
Extracción JSON column->>'key' JSON_VALUE(column, '$.key')
Upsert INSERT ... ON CONFLICT DO UPDATE Instrucción MERGE
Tipo de matriz INTEGER[] No hay matrices nativas. Utiliza parámetros con valores de tabla.
Distinción entre mayúsculas y minúsculas Distingue entre mayúsculas y minúsculas de forma predeterminada Por defecto, insensible a mayúsculas minúsculas (depende de la recopilación).

Obtén el identificador insertado

Sustituye la cláusula de RETURNING PostgreSQL por la cláusula 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

Sustituye la sintaxis LIMIT/OFFSET por la sintaxis 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"

Migrar desde pgx (PostgreSQL)

El pgx controlador utiliza su propio pool de conexiones y API que difieren de database/sql. Para migrar, cambia a la interfaz estándar database/sql .

Cambio de pool pgx a base de datos/sql

Sustituye la pgxpool API por la interfaz estándar 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))

Sustituir características específicas de pgx

Función PGX Equivalente a go-mssqldb
pgx.CollectRows Manual rows.Next() y rows.Scan() bucle.
pgx.RowToStructByName Manual rows.Scan para entrar en campos estructurales.
Consultas por lotes (pgx.Batch) Varias llamadas de ExecContext o procedimientos almacenados.
COPY FROM para inserción a granel mssql.CopyIn Para inserción a granel.
pgx.ConnConfig msdsn.Config o cadenas de conexión basadas en URL.
pgxpool.Pool sql.DB con SetMaxOpenConns y SetMaxIdleConns.

Migrar desde go-sql-driver/mysql (MySQL)

Intercambia la importación MySQL y el nombre del controlador

Sustituye la importación de MySQL y el nombre del controlador por 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 el formato MySQL DSN

Convierte el formato MySQL DSN a una URL de SQL Server.

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

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

Sustituir los marcadores de posición con signos de interrogación

MySQL utiliza ? marcadores posicionales.

// 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))

Comparar la sintaxis de MySQL y SQL Server

Operation MySQL SQL Server
Incremento automático AUTO_INCREMENT IDENTITY(1,1)
Inserta el DNI LAST_INSERT_ID() SCOPE_IDENTITY() o OUTPUT INSERTED.id
Limitar filas LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Marca de tiempo actual NOW() GETUTCDATE()
Si es nulo IFNULL(expr, default) ISNULL(expr, default) o COALESCE(expr, default)
Longitud de cadena LENGTH(str) LEN(str)
Subcadena SUBSTRING(str, start, len) SUBSTRING(str, start, len) (igual)
Formato de fecha DATE_FORMAT(d, '%Y-%m-%d') FORMAT(d, 'yyyy-MM-dd') o CONVERT(VARCHAR, d, 23)
Upsert INSERT ... ON DUPLICATE KEY UPDATE Instrucción MERGE
Entrecomillado con comillas invertidas `column` [column]

Sustituir la sintaxis específica de MySQL

Sintaxis MySQL Equivalente a SQL Server
AUTO_INCREMENT IDENTITY(1,1)
LIMIT n OFFSET m OFFSET m ROWS FETCH NEXT n ROWS ONLY (requiere ORDER BY)

AUTO_INCREMENT a 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
);

LÍMITE PARA EL DESPLAZAMIENTO/RECADACIÓN:

-- 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;

Patrones comunes de migración

Consigue la última identidad insertada

Utilice la cláusula OUTPUT o SCOPE_IDENTITY() para recuperar el valor de identidad de la fila insertada:

// 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

Prefiere OUTPUT en lugar de SCOPE_IDENTITY(). La OUTPUT cláusula funciona con inserciones por lotes y no depende del orden de las instrucciones.

Upsert (insertar o actualizar)

El ON CONFLICT de PostgreSQL y el ON DUPLICATE KEY UPDATE de MySQL se asignan al 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))

Inserción masiva

Sustituye los PostgreSQL COPY o MySQL LOAD DATA por 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()

Manejar valores NULL

Todos los controladores de base de datos de Go gestionan los valores NULL de la misma manera porque usan los tipos database/sql .

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

Todos los controladores de bases de datos de Go usan la misma database/sql API de transacciones.

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()

Lista de comprobación para la migración

Step Action
Instalación del controlador go get github.com/microsoft/go-mssqldb
Cambiar importaciones Sustituye la antigua importación del controlador por _ "github.com/microsoft/go-mssqldb".
Actualización de cadenas de conexión Cambio al formato de URL de SQL Server: sqlserver://user:pass@host?database=db.
Reemplazar marcadores de posición de parámetros Cambiar $1/$2 o ? con .@namesql.Named
Actualizar la sintaxis SQL Cambiar LIMIT/OFFSET, RETURNING, NOW(), SERIAL, y otros SQL específicos de la base de datos.
Reemplazar operaciones masivas Cambiar COPY o LOAD DATA a mssql.CopyIn.
Actualizar el esquema DDL Cambiar SERIAL/AUTO_INCREMENT por IDENTITY, TEXT por NVARCHAR, BOOLEAN por BIT.
Prueba todas las consultas Ejecuta tu suite de pruebas contra una instancia de SQL Server para detectar diferencias de sintaxis.
Configuración del cifrado Añadir encrypt=true (o depender de la detección automática de Azure) para producción.
Configuración de la autenticación Configura Microsoft Entra ID para Azure o la autenticación de SQL Server para entornos locales.