Mappature di tipo di dati go-mssqldb

Il go-mssqldb driver converte automaticamente i tipi Go in tipi SQL Server quando si passano i parametri, e riconverte i tipi SQL Server in tipi Go quando si scansionano i risultati. Questo articolo documenta le conversioni predefinite e i tipi specifici per driver disponibili per un controllo esplicito.

Gli esempi in questo articolo vengono confrontati con il database di esempio AdventureWorks2025 . Esempi orientati alla lettura interrogano oggetti integrati come Production.Product e Sales.vSalesPerson. Gli esempi orientati alla scrittura mirano HumanResources.Department a .Production.ProductCategory

Vai su SQL Server parameter mappings

Quando passi valori come parametri di query, il driver converte i tipi Go in tipi SQL Server:

Tipo Go Tipo SQL Server Note
string nvarchar Stringa Unicode. Usa mssql.VarChar per varchar non-Unicode.
[]byte varbinary Dati binari.
int64 bigint
float64 float Float a 64 bit.
bool pezzo
time.Time datetimeoffset Preserva lo spostamento del fuso orario. Usa mssql.DateTime1 per l'appuntamento. Usare mssql.DateTimeOffset per un controllo esplicito dello spostamento.
int32 int
int16 smallint
int8 tinyint
float32 real Flottante a 32 bit.
mssql.TVP Parametro con valori di tipo tabella Vedi Parametri a valori della tabella.

Tipi specifici per driver

Usa questi tipi dal mssql pacchetto quando hai bisogno di un controllo esplicito sul tipo SQL Server:

Tipo di driver Tipo SQL Server Description
mssql.VarChar varchar Stringa non Unicode. Avvolgi un string valore.
mssql.NVarCharMax nvarchar(max) Stringa grande Unicode.
mssql.VarCharMax varchar(max) Stringa grande non Unicode.
mssql.NChar nchar Stringa Unicode a lunghezza fissa. Avvolgi un string valore.
mssql.DateTime1 datetime Datatime legacy senza offset.
mssql.DateTimeOffset datetimeoffset Controllo esplicito dello spostamento.
mssql.NullDate date Valore solo data nullabile.
mssql.NullTime time Valore solo temporale nullabile.
mssql.NullDateTime datetime2 Valore di data e ora nullabile senza conversione di fuso orario.
mssql.UniqueIdentifier uniqueidentifier Valore GUID.
mssql.TVP Tipo di tabella definito dall'utente Parametro a valori di tabella struct.

Usa i tipi di aiuto data e ora nullabili quando hai bisogno di semantica solo data o solo tempo di SQL Server e il parametro o la destinazione di scansione potrebbero essere NULL.

Esempio: varchar vs. nvarchar

Di default, i string parametri vengono inviati come nvarchar. Da usare varchar invece:

import "github.com/microsoft/go-mssqldb"

// Sends as nvarchar (default)
db.QueryContext(ctx, "SELECT * FROM Production.Product WHERE Name = @p1", "Adjustable Race")

// Sends as varchar
db.QueryContext(ctx, "SELECT * FROM Production.Product WHERE Name = @p1", mssql.VarChar("Adjustable Race"))

Esempio: unicidentificatore

Usa il mssql.UniqueIdentifier tipo per scansionare uniqueidentifier le colonne:

import "github.com/microsoft/go-mssqldb"

var id mssql.UniqueIdentifier
err := db.QueryRowContext(ctx, "SELECT rowguid FROM Production.Product WHERE Name = @p1",
    sql.Named("p1", "Adjustable Race")).Scan(&id)

Mappaggi dei risultati di SQL Server to Go

Quando scansioni i risultati delle query, il driver converte i tipi di SQL Server in tipi Go:

Tipo SQL Server Tipo Go Note
int, bigint, smallint,tinyint int64 Tutti i tipi di interi scansionano in int64. Invasa il congedo secondo necessità.
float, reale float64
decimale, numerico, denaro, piccolo denaro []byte oppure string Nessun tipo decimale nativo Go. Scansiona o string usa una libreria decimale di terze parti.
pezzo bool
Char, Varchar, testo string
nchar, nvarchar, ntext string
binario, varbinario, immagine []byte
data, datatime, datetime2, smalldatetime time.Time
datetimeoffset time.Time Lo sfasamento del fuso orario viene mantenuto.
time time.Time La componente della data è 0001-01-01.
uniqueidentifier []byte oppure mssql.UniqueIdentifier Scansiona in byte raw di default. Da usare mssql.UniqueIdentifier per output GUID formattato.
sql_variant interface{} Il tipo di Go in concreto dipende dal valore memorizzato di SQL Server.
xml string

Parametri denominati

Usare sql.Named per creare parametri nominati:

rows, err := db.QueryContext(ctx,
    "SELECT * FROM Sales.vSalesPerson WHERE CountryRegionName = @location AND FirstName = @name",
    sql.Named("location", "Australia"),
    sql.Named("name", "Jared"))

Note

Anche i parametri posizionali (@p1, @p2) funzionano. Il driver assegna nomi ordinali in base all'ordine degli argomenti.

Gestire i valori NULL

Le colonne di SQL Server che permettono NULL richiedono una gestione speciale in Go. Il pacchetto standard database/sql fornisce tipi nullabili a questo scopo.

SQL. Tipi nulli

Usare sql.NullString, sql.NullInt64, sql.NullFloat64, sql.NullBool, e sql.NullTime per gestire colonne che possono essere NULL. Per i valori di data, ora e datetime2 di SQL Server che vuoi mantenere separati dai valori predefiniti di time.Time Go, usa mssql.NullDate, mssql.NullTime, e mssql.NullDateTime:

var name sql.NullString
var color sql.NullString
var weight sql.NullFloat64
var sellStartDate sql.NullTime
var sellEndDate sql.NullTime

err := db.QueryRowContext(ctx,
    "SELECT Name, Color, Weight, SellStartDate, SellEndDate FROM Production.Product WHERE ProductID = @id",
    sql.Named("id", 1)).Scan(&name, &color, &weight, &sellStartDate, &sellEndDate)
if err != nil {
    log.Fatal(err)
}

if name.Valid {
    fmt.Println("Name:", name.String)
} else {
    fmt.Println("Name: NULL")
}

if weight.Valid {
    fmt.Printf("Weight: %.2f\n", weight.Float64)
}

Gestione NULL basata su puntatore

Come alternativa ai sql.Null tipi, usa i puntatori. Un nil puntatore rappresenta NULL:

var name *string
var color *string

err := db.QueryRowContext(ctx,
    "SELECT Name, Color FROM Production.Product WHERE ProductID = @id",
    sql.Named("id", 1)).Scan(&name, &color)

if name != nil {
    fmt.Println("Name:", *name)
} else {
    fmt.Println("Name: NULL")
}

Tip

La gestione NULL basata su pointer è più concisa, ma sql.Null i tipi rendono l'intento NULL più esplicito nelle definizioni delle struct. Scegli il pattern che la tua squadra preferisce e usalo con costanza.

Invia valori NULL come parametri

Pass nil per inviare un valore NULL come parametro:

// Insert a row with a NULL Color.
_, err := db.ExecContext(ctx,
    "INSERT INTO Production.ProductCategory (Name) VALUES (@name)",
    sql.Named("name", "Custom Parts"))

Precisione decimale e numerica

I SQL Server decimal e numeric i tipi supportano fino a 38 cifre di precisione. Go float64 fornisce solo circa 15-16 cifre significative. Scansionare valori float64 ad alta precisione provoca perdita di precisione silenziosa.

Scansione a stringa

L'approccio più sicuro e semplice è scansionare decimal/numeric/money/smallmoney le colonne in string, poi convertire con una libreria sicura per precisione:

var priceStr string
err := db.QueryRowContext(ctx,
    "SELECT ListPrice FROM Production.Product WHERE ProductID = @id",
    sql.Named("id", 1)).Scan(&priceStr)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Price:", priceStr) // "12345.678901234567890"

Per percorsi sensibili alle prestazioni, puoi scansionare e []byte parizzare direttamente con la tua libreria decimale per ridurre le allocazioni di stringhe.

Usa shopspring/decimal per l'aritmetica

La shopspring/decimal libreria fornisce aritmetica decimale di precisione arbitraria:

import "github.com/shopspring/decimal"

var priceStr string
err := db.QueryRowContext(ctx,
    "SELECT ListPrice FROM Production.Product WHERE ProductID = @id",
    sql.Named("id", 1)).Scan(&priceStr)
if err != nil {
    log.Fatal(err)
}

price, err := decimal.NewFromString(priceStr)
if err != nil {
    log.Fatal(err)
}

tax := price.Mul(decimal.NewFromFloat(0.08))
total := price.Add(tax)
fmt.Println("Total:", total.StringFixed(2))

Attenzione

Non scansionare money mai le decimal colonne quando float64 la precisione esatta conta (calcoli finanziari, valuta, tasse). Usa la scansione a valore esatto (string o []byte) con shopspring/decimal o cockroachdb/apd invece.

Inviare valori decimali come parametri

Quando si inviano valori decimali come parametri, si usa string per evitare la perdita di precisione del float:

price := "12345.678901234567890"
_, err := db.ExecContext(ctx,
    "UPDATE Production.Product SET ListPrice = @price WHERE ProductID = @id",
    sql.Named("price", price),
    sql.Named("id", 1)) // String is converted to decimal by SQL Server.

Tipi di data e ora

tempo. Mappatura temporale

Il driver mappa di default Go time.Time a datetimeoffset Go a , il che preserva lo offset del fuso orario. Usa tipi specifici per driver per altri formati di datetime:

Valore Go Tipo SQL Server Quando utilizzare
time.Time datetimeoffset Predefinito Usalo quando il fuso orario conta.
mssql.DateTime1(t) datetime Colonne legacy che non supportano l'offset.
mssql.DateTimeOffset(t) datetimeoffset Controllo esplicito dello spostamento.

Migliori pratiche UTC

Memorizza i tempi in UTC e converti in ora locale nel livello applicativo:

now := time.Now().UTC()
_, err := db.ExecContext(ctx,
    "INSERT INTO Production.ScrapReason (Name, ModifiedDate) VALUES (@name, @modified)",
    sql.Named("name", "Operator error"),
    sql.Named("modified", now))

Pattern univoci (GUID)

SQL Server memorizza uniqueidentifier i valori con i primi tre gruppi in ordine little-endian dei byte (noto anche come ordine mixed-endian o GUID). Se scansioni una uniqueidentifier colonna in []byte, i byte grezzi non corrispondono alla rappresentazione standard della stringa. Usa mssql.UniqueIdentifier invece, che gestisce automaticamente la conversione dell'ordine dei byte.

Scansiona con mssql. UnicoIdentificatore

Di default, uniqueidentifier le colonne scansionano verso []byte. Uso mssql.UniqueIdentifier per stringhe GUID formattate:

var id mssql.UniqueIdentifier
err := db.QueryRowContext(ctx,
    "SELECT rowguid FROM Production.Product WHERE Name = @name",
    sql.Named("name", "Adjustable Race")).Scan(&id)
if err != nil {
    log.Fatal(err)
}
fmt.Println("ID:", id) // "6F9619FF-8B86-D011-B42D-00C04FC964FF"

Generare e inviare GUID

Utilizzare NEWID() sul server o generare in Go:

// Server-side generation
var newID mssql.UniqueIdentifier
err := db.QueryRowContext(ctx,
    "INSERT INTO Production.ProductCategory (Name) OUTPUT INSERTED.rowguid VALUES (@name)",
    sql.Named("name", "Custom Parts")).Scan(&newID)

Generazione lato client con il google/uuid package:

import "github.com/google/uuid"

id := mssql.UniqueIdentifier(uuid.New())
_, err := db.ExecContext(ctx,
    "UPDATE Production.ProductCategory SET rowguid = @id WHERE Name = @name",
    sql.Named("id", id),
    sql.Named("name", "Custom Parts"))

Lista di controllo per la mappatura dei tipi

Scenario Approccio consigliato
Colonne nullabili Usa sql.NullString, sql.NullInt64, sql.NullFloat64, sql.NullBool, sql.NullTime, o tipi di puntatori.
Valori finanziari/decimali Scansiona in string, usa shopspring/decimal per l'aritmetica.
Date con fusi orari Usa time.Time (mappa a datetimeoffset di default).
Colonne datatime ereditarie Usare mssql.DateTime1 quando invio parametri.
GUID Scansiona con mssql.UniqueIdentifier per l'output formattato.
Stringhe non Unicode Usalo mssql.VarChar per evitare conversioni implicite nvarchar .
Colonne JSON Scansiona per string, smaschera con encoding/json. Vedi dati JSON e XML.
Colonne XML Scansiona per string, smaschera con encoding/xml. Vedi dati JSON e XML.