Migrar para go-mssqldb a partir de outros drivers

Este guia ajuda os programadores do Go a migrar do PostgreSQL, MySQL e outros drivers de base de dados para go-mssqldb o SQL Server. Cobre diferenças de sintaxe SQL, alterações específicas do driver e padrões equivalentes para operações comuns.

Note

Os exemplos deste artigo são executados na base de dados de exemplo AdventureWorks2025.

Porque migrar para o SQL Server com go-mssqldb

Feature SQL Server com go-mssqldb
Segurança empresarial Sempre encriptado, segurança ao nível de linha, mascaramento dinâmico de dados, encriptação transparente de dados (TDE).
Authentication Microsoft Entra ID (anteriormente Azure AD), identidade gerida, Windows authentication, Kerberos.
Integração com Azure Suporte nativo para Base de Dados SQL do Azure com encriptação automática, ligações sem palavra-passe e gestão de failover.
Performance Índices columnstore, OLTP na memória, arquivo de consultas para correção automática do plano.
Formatos de dados Suporte integrado a JSON e XML com indexação e consulta do lado do servidor.
Tooling Extensões SQL SQL Server Management Studio (SSMS), Azure Data Studio, Visual Studio Code SQL.

Lista de verificação para considerações de migração

Reveja estes itens antes de começar a migrar as consultas linha a linha:

Consideração O que mudar
A sintaxe dos marcadores de posição difere da de muitos controladores Go. Substitua ?, $1 e marcadores de posição semelhantes por @name ou @p1, em seguida passe os argumentos sql.Named() quando apropriado.
LastInsertId() não é suportado. Use OUTPUT INSERTED.<column> ou SELECT SCOPE_IDENTITY() em vez disso.
Os valores Go string são mapeados, por defeito, para tipos Unicode. Pressuponha a semântica de nvarchar, a menos que escolha um tipo específico do controlador, como mssql.VarChar, para dados que não sejam Unicode.
As tabelas temporárias estão limitadas à ligação física. Mantenha a lógica de criar e usar uma tabela temporária na mesma ligação ou dentro da mesma transação, se as instruções posteriores dependerem dessa tabela.
A autenticação do Microsoft Entra utiliza um registo do controlador diferente. Importa github.com/microsoft/go-mssqldb/azuread, usa sql.Open("azuresql", ...), e define explicitamente as opções TLS do SQL do Azure com encrypt=true&TrustServerCertificate=false.

Para orientações mais aprofundadas nestas áreas, consulte Consultas e declarações, Limitações, Mapeamentos de tipos de dados, autenticação do Microsoft Entra ID e Resolução de problemas.

Migrar a partir de lib/pq (PostgreSQL)

Alterar o nome da importação e do piloto

Substitua a importação lib/pq e o nome do driver 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)

Alterar o formato da cadeia de ligação

Converter o formato chave-valor do PostgreSQL para uma URL do 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"

Altere os marcadores de posição de parâmetros

O PostgreSQL usa $1, $2 marcadores posicionais. O SQL Server utiliza parâmetros nomeados com @:

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

Alterações na sintaxe SQL

Operation PostgreSQL SQL Server
Incremento automático SERIAL ou GENERATED ALWAYS AS IDENTITY IDENTITY(1,1)
Insira o ID RETURNING id OUTPUT INSERTED.id ou SELECT SCOPE_IDENTITY()
Tipo booleano BOOLEAN BIT
Concatenação de cordas \|\| + ou CONCAT()
Carimbo de data/hora atual NOW() ou CURRENT_TIMESTAMP GETUTCDATE() ou SYSDATETIMEOFFSET()
Limitar linhas LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Tipo de string TEXT ou VARCHAR NVARCHAR(MAX) ou NVARCHAR(n)
Extração JSON column->>'key' JSON_VALUE(column, '$.key')
Upsert INSERT ... ON CONFLICT DO UPDATE Declaração MERGE
Tipo de matriz INTEGER[] Sem matrizes nativas. Usa parâmetros com valores de tabela.
Sensível às maiúsculas e minúsculas Sensível a maiúsculas minúsculas por defeito Não distingue entre maiúsculas e minúsculas por predefinição (depende da ordenação).

Obter o ID introduzido

Substitua a cláusula do RETURNING PostgreSQL pela cláusula do 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

Substitua a sintaxe LIMIT/OFFSET pela sintaxe 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 a partir de pgx (PostgreSQL)

O pgx driver utiliza o seu próprio pool de ligações e API, que diferem de database/sql. Para migrar, mude para a interface padrão database/sql .

Mudança de pool pgx para base de dados/sql

Substitua a pgxpool API pela interface padrão 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))

Substituir funcionalidades específicas do pgx

funcionalidade pgx Equivalente a go-mssqldb
pgx.CollectRows Manual rows.Next() e rows.Scan() ciclo.
pgx.RowToStructByName Manualmente rows.Scan nos campos da estrutura.
Consultas em lote (pgx.Batch) Várias chamadas ExecContext ou procedimentos armazenados.
COPY FROM para inserção a granel mssql.CopyIn Para inserção a granel.
pgx.ConnConfig msdsn.Config ou cadeias de ligação baseadas em URL.
pgxpool.Pool sql.DB com SetMaxOpenConns e SetMaxIdleConns.

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

Troque a importação do MySQL e o nome do driver

Substitua a instrução de importação do MySQL e o nome do driver 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)

Converter o formato DSN do MySQL

Converta o formato MySQL DSN para uma URL do SQL Server.

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

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

Substituir os marcadores de posição com ponto de interrogação

O MySQL utiliza ? marcadores posicionais.

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

Compare a sintaxe do MySQL e do SQL Server

Operation MySQL SQL Server
Incremento automático AUTO_INCREMENT IDENTITY(1,1)
Insira o ID LAST_INSERT_ID() SCOPE_IDENTITY() ou OUTPUT INSERTED.id
Limitar linhas LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Carimbo de data/hora atual NOW() GETUTCDATE()
Se nulo IFNULL(expr, default) ISNULL(expr, default) ou COALESCE(expr, default)
Comprimento da cadeia LENGTH(str) LEN(str)
Substring SUBSTRING(str, start, len) SUBSTRING(str, start, len) (mesmo)
Formato de data DATE_FORMAT(d, '%Y-%m-%d') FORMAT(d, 'yyyy-MM-dd') ou CONVERT(VARCHAR, d, 23)
Upsert INSERT ... ON DUPLICATE KEY UPDATE Declaração MERGE
Citação de backticks `column` [column]

Substituir a sintaxe específica do MySQL

Sintaxe MySQL Equivalente ao SQL Server
AUTO_INCREMENT IDENTITY(1,1)
LIMIT n OFFSET m OFFSET m ROWS FETCH NEXT n ROWS ONLY (necessita de ORDER BY)

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

Padrões migratórios comuns

Obter o último identificador inserido

Utilize a cláusula OUTPUT ou SCOPE_IDENTITY() para recuperar o valor de identidade da linha inserida:

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

Prefira a OUTPUT cláusula em vez de SCOPE_IDENTITY(). A cláusula OUTPUT funciona com inserções por lotes e não depende da ordenação das instruções.

Upsert (inserir ou atualizar)

O ON CONFLICT PostgreSQL e o ON DUPLICATE KEY UPDATE MySQL correspondem ao MERGE 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))

Inserção em massa

Substitua PostgreSQL COPY ou 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()

Controlar valores NULL

Todos os drivers de base de dados do Go tratam os valores NULL da mesma forma porque usam os 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 os drivers da base de dados Go usam a mesma database/sql API de transações.

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 verificação da migração

Step Action
Instale o controlador go get github.com/microsoft/go-mssqldb
Alterar importações Substitua a importação do antigo driver por _ "github.com/microsoft/go-mssqldb".
Atualizar cadeias de ligação Alteração para o formato URL do SQL Server: sqlserver://user:pass@host?database=db.
Substituir marcadores de parâmetros Altere $1/$2 ou ? para @name com sql.Named.
Atualizar sintaxe SQL Alterar LIMIT/OFFSET, RETURNING, NOW(), SERIAL, e outros SQL específicos da base de dados.
Substituir operações a grande escala Mudança COPY ou LOAD DATA para mssql.CopyIn.
Atualizar esquema DDL Muda SERIAL/AUTO_INCREMENT para IDENTITY, TEXT para NVARCHAR, BOOLEAN para BIT.
Teste todas as consultas Executa o teu conjunto de testes contra uma instância do SQL Server para detetar diferenças de sintaxe.
Configurar criptografia Adicionar encrypt=true (ou confiar na deteção automática do Azure) para produção.
Configurar a autenticação Configure o Microsoft Entra ID para Azure, ou a autenticação do SQL Server para instalações locais.