Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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. |