Migra a go-mssqldb da altri driver

Questa guida aiuta gli sviluppatori di Go a migrare da PostgreSQL, MySQL e altri driver di database a go-mssqldb SQL Server. Copre differenze nella sintassi SQL, modifiche specifiche per driver e pattern equivalenti per operazioni comuni.

Annotazioni

Gli esempi in questo articolo vengono confrontati con il database di esempio AdventureWorks2025 .

Perché migrare a SQL Server con go-mssqldb

Feature SQL Server con go-mssqldb
Sicurezza aziendale Sempre criptato, sicurezza a livello di riga, mascheramento dinamico dei dati, crittografia trasparente dei dati (TDE).
Authentication Microsoft Entra ID (precedentemente Azure AD), identità gestita, autenticazione di Windows, Kerberos.
Integrazione con Azure Supporto nativo database SQL di Azure con crittografia automatica, connessioni senza password e gestione del failover.
Performance Indici Column Store, OLTP in memoria, query store per correzione automatica del piano.
Formati di dati Supporto JSON e XML integrato con indicizzazione e query lato server.
Tooling SQL Server Management Studio (SSMS), Azure Data Studio, estensioni SQL per Visual Studio Code.

Lista di controllo per le considerazioni sulla migrazione

Rivedi questi elementi prima di iniziare a trasferire query riga per riga:

Consideration Cosa modificare
La sintassi dei segnaposto differisce da quella di molti driver Go. Sostituire ?, $1 e segnaposto simili con @name o @p1, quindi passare argomenti sql.Named() ove appropriato.
LastInsertId() non è supportata. In sostituzione utilizzare OUTPUT INSERTED.<column> o SELECT SCOPE_IDENTITY().
I valori Go string vengono mappati di default ai tipi Unicode. Presupponi la semantica di nvarchar, a meno che tu non scelga un tipo specifico del driver, come mssql.VarChar per dati non Unicode.
Le tabelle temporanee sono limitate alla connessione fisica. Mantieni la logica di creazione e uso della tabella temporanea sulla stessa connessione o all'interno della stessa transazione se le istruzioni successive dipendono da quella tabella.
L'autenticazione Microsoft Entra utilizza una registrazione diversa del driver. Importa github.com/microsoft/go-mssqldb/azuread, usa sql.Open("azuresql", ...), e imposta esplicitamente le opzioni Azure SQL TLS con encrypt=true&TrustServerCertificate=false.

Per indicazioni più approfondite su queste aree, consulta Query e istruzioni, Limitazioni, Mappature dei tipi di dati, Autenticazione di Microsoft Entra ID e Risoluzione dei problemi.

Esegui la migrazione da lib/pq (PostgreSQL)

Cambia l'importazione e il nome del driver

Sostituisci il nome dell'importazione lib/pq e del driver con 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)

Modifica il formato della stringa di connessione

Converti il formato key-value di PostgreSQL in un URL di 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"

Modifica i segnaposto dei parametri

PostgreSQL utilizza $1, $2 segnaposto posizionali. SQL Server utilizza parametri nominati 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))

Cambiamenti nella sintassi SQL

Operation PostgreSQL SQL Server
Incremento automatico SERIAL oppure GENERATED ALWAYS AS IDENTITY IDENTITY(1,1)
Inserisci un ID RETURNING id OUTPUT INSERTED.id oppure SELECT SCOPE_IDENTITY()
Tipo booleano BOOLEAN BIT
Concatenazione di stringhe \|\| + oppure CONCAT()
Timestamp attuale NOW() oppure CURRENT_TIMESTAMP GETUTCDATE() oppure SYSDATETIMEOFFSET()
Righe limite LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Tipo di string TEXT oppure VARCHAR NVARCHAR(MAX) oppure NVARCHAR(n)
Estrazione JSON column->>'key' JSON_VALUE(column, '$.key')
Upsert INSERT ... ON CONFLICT DO UPDATE MERGE istruzione
Tipo di array INTEGER[] Nessun array nativo. Usa parametri a valori di tabella.
Distinzione tra maiuscole e minuscole Con distinzione tra maiuscole e minuscole per impostazione predefinita Non distingue tra maiuscole e minuscole per impostazione predefinita (dipende dalla collazione).

Ottieni l'ID inserito

Sostituisci la clausola di RETURNING PostgreSQL con quella di 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

Sostituisci la sintassi LIMIT/OFFSET con la sintassi 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"

Esegui la migrazione da pgx (PostgreSQL)

Il pgx driver utilizza il proprio pool di connessione e API che differiscono da database/sql. Per migrare, passa all'interfaccia standard database/sql .

Passaggio da pgx pool a database/sql

Sostituisci l'API pgxpool con l'interfaccia 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))

Sostituire le funzionalità specifiche del pgx

Funzione PGX equivalente di go-mssqldb
pgx.CollectRows Ciclo manuale rows.Next() e rows.Scan().
pgx.RowToStructByName Inserisci manualmente rows.Scan nei campi della struct.
Query in batch (pgx.Batch) chiamate multiple ExecContext o procedure archiviate.
COPY FROM per inserimento in blocco mssql.CopyIn per inserimento in grandi quantità.
pgx.ConnConfig msdsn.Config o stringhe di connessione basate su URL.
pgxpool.Pool sql.DB con SetMaxOpenConns e SetMaxIdleConns.

Esegui la migrazione da go-sql-driver/mysql (MySQL)

Scambia l'importazione MySQL e il nome del driver

Sostituisci l'importazione MySQL e il nome del driver con 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)

Converti il formato MySQL DSN

Converti il formato MySQL DSN in un URL di SQL Server.

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

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

Sostituire segnaposto con punto interrogativo

MySQL utilizza ? segnaposto posizionali.

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

Confronta la sintassi di MySQL e SQL Server

Operation MySQL SQL Server
Incremento automatico AUTO_INCREMENT IDENTITY(1,1)
Inserisci un ID LAST_INSERT_ID() SCOPE_IDENTITY() oppure OUTPUT INSERTED.id
Righe limite LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
Timestamp attuale NOW() GETUTCDATE()
Se è null IFNULL(expr, default) ISNULL(expr, default) oppure COALESCE(expr, default)
Lunghezza della corda LENGTH(str) LEN(str)
Sottostringa SUBSTRING(str, start, len) SUBSTRING(str, start, len) (stesso)
Formato data DATE_FORMAT(d, '%Y-%m-%d') FORMAT(d, 'yyyy-MM-dd') oppure CONVERT(VARCHAR, d, 23)
Upsert INSERT ... ON DUPLICATE KEY UPDATE MERGE istruzione
Citazione del backtick `column` [column]

Sostituire la sintassi specifica di MySQL

Sintassi MySQL Equivalente di SQL Server
AUTO_INCREMENT IDENTITY(1,1)
LIMIT n OFFSET m OFFSET m ROWS FETCH NEXT n ROWS ONLY (richiede ORDER BY)

AUTO_INCREMENT su 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 in 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;

Modelli di migrazione comuni

Recupera l'ultimo ID inserito

Usa la OUTPUT clausola oppure SCOPE_IDENTITY() per recuperare il valore identità della riga inserita:

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

Preferisci la OUTPUT clausola rispetto a SCOPE_IDENTITY(). La clausola OUTPUT funziona con gli inserimenti in batch e non dipende dall'ordine delle istruzioni.

Upsert (inserire o aggiornare)

ON CONFLICT di PostgreSQL e ON DUPLICATE KEY UPDATE di MySQL corrispondono a MERGE di 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))

Inserimento in blocco

Sostituisci COPY di PostgreSQL o LOAD DATA di MySQL con 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()

Gestire i valori NULL

Tutti i driver database Go gestiscono i valori NULL allo stesso modo perché usano i database/sql tipi.

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

Tutti i driver database Go utilizzano la stessa database/sql API di transazione.

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

Elenco di controllo per la migrazione

Passo Action
Installare il driver go get github.com/microsoft/go-mssqldb
Cambiare importazioni Sostituisci il vecchio driver importato con _ "github.com/microsoft/go-mssqldb".
Aggiornare le stringhe di connessione Cambio al formato URL di SQL Server: sqlserver://user:pass@host?database=db.
Sostituire i segnaposto dei parametri Cambia $1/$2 o ? a @name con .sql.Named
Aggiorna la sintassi SQL Modifica LIMIT/OFFSET, RETURNING, NOW(), SERIAL, e altri SQL specifici del database.
Sostituisci le operazioni in blocco Cambia COPY o LOAD DATA a mssql.CopyIn.
Schema di aggiornamento DDL Cambia SERIAL/AUTO_INCREMENT in IDENTITY, TEXT in NVARCHAR, BOOLEAN in BIT.
Verifica tutte le query Esegui la tua suite di test su un'istanza di SQL Server per individuare le differenze di sintassi.
Configurare la crittografia Aggiungi encrypt=true (o usa il rilevamento automatico di Azure) per la produzione.
Configurare l'autenticazione Configura Microsoft Entra ID per Azure oppure l'autenticazione di SQL Server per ambienti locali.