Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
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. |