Mapowania typów danych go-mssqldb

Sterownik go-mssqldb automatycznie konwertuje typy Go na typy SQL Server po przekazaniu parametrów, a podczas skanowania wyników przekształca typy SQL Server z powrotem na typy Go. Ten artykuł dokumentuje domyślne konwersje oraz specyficzne dla sterowników typy dostępne do jawnej kontroli.

Przykłady w tym artykule porównane są z przykładową bazą danych AdventureWorks2025 . Przykłady zorientowane na odczyt zapytania do wbudowanych obiektów, takich jak Production.Product i Sales.vSalesPerson. Przykłady zorientowane na zapis skierowane do HumanResources.Department i Production.ProductCategory.

Przejdź do mapowania parametrów SQL Server

Gdy przekazujesz wartości jako parametry zapytania, sterownik konwertuje typy Go na typy SQL Server:

typ Go Typ programu SQL Server Notatki
string nvarchar Ciąg unicode. Zastosowanie mssql.VarChar dla varcharów nie-Unicode.
[]byte varbinary Dane binarne.
int64 bigint
float64 float 64-bitowy float.
bool bit
time.Time datetimeoffset Zachowuje przesunięcie strefy czasowej. Wykorzystanie mssql.DateTime1 na randkę. Zastosowanie mssql.DateTimeOffset do wyraźnej kontroli przesunięcia.
int32 int
int16 smallint
int8 tinyint
float32 prawdziwy 32-bitowy float.
mssql.TVP Parametr z wartością tabeli Zobacz parametry tabelowe.

Typy specyficzne dla kierowców

Używaj tych typów z pakietumssql, gdy potrzebujesz wyraźnej kontroli nad typem SQL Server:

Typ sterownika Typ programu SQL Server Description
mssql.VarChar varchar Ciąg nie-Unicode. Opakuj wartość string .
mssql.NVarCharMax nvarchar(max) Duży ciąg znaków w Unicode.
mssql.VarCharMax varchar(max) Nie-Unicode duży ciąg znaków.
mssql.NChar nchar Ciąg Unicode o stałej długości. Opakuj wartość string .
mssql.DateTime1 datetime Starszy czas randkowy bez przesunięcia.
mssql.DateTimeOffset datetimeoffset Wyraźna kontrola offsetu.
mssql.NullDate date Wartość nulowalna tylko na datę.
mssql.NullTime time Wartość tylko czasowa do unieważnienia.
mssql.NullDateTime datetime2 Wartość data i czasu do zerowania bez konwersji stref czasowych.
mssql.UniqueIdentifier uniqueidentifier Wartość identyfikatora GUID.
mssql.TVP Typ tabeli zdefiniowany przez użytkownika Struktura parametrów tabelowych.

Używaj nullable date-and-time helperów, gdy potrzebujesz semantyki tylko daty lub czasu w SQL Server, a parametr lub cel skanowania może być NULL.

Przykład: varchar kontra nvarchar

Domyślnie string parametry są wysyłane jako nvarchar. Zamiast tego można użyć varchar :

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

Przykład: unikalny identyfikator

Użyj czcionki mssql.UniqueIdentifier do skanowania uniqueidentifier kolumn:

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)

Mapowania wyników SQL Server to Go

Podczas skanowania wyników zapytań sterownik konwertuje typy SQL Server na typy Go:

Typ programu SQL Server typ Go Notatki
int, bigint, smallint, tinyint int64 Wszystkie typy liczb całkowitych skanują do .int64 Rzucaj według potrzeby.
Float, real float64
dziesiętne, liczbowe, pieniądze, drobne pieniądze []byte lub string Nie ma natywnego typu dziesiętnego w Go. Skanuj lub korzystaj z string zewnętrznej biblioteki dziesiętnej.
bit bool
Char, Varchar, tekst string
nchar, nvarchar, ntext string
binarny, varbinary, obraz []byte
randka, randka,randka2,mała randkagodzina time.Time
datetimeoffset time.Time Przesunięcie strefy czasowej jest zachowane.
time time.Time Składowa daty to 0001-01-01.
uniqueidentifier []byte lub mssql.UniqueIdentifier Domyślnie skanuje do surowych bajtów. Zastosowanie mssql.UniqueIdentifier do formatowanego wyjścia GUID.
sql_variant interface{} Konkretny typ Go zależy od zapisanej wartości SQL Server.
xml string

Nazwane parametry

Użycie sql.Named do tworzenia nazwanych parametrów:

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

Note

Parametry pozycyjne (@p1, @p2) również działają. Sterownik przypisuje nazwy porządkowe na podstawie kolejności argumentów.

Obsługa wartości NULL

Kolumny SQL Server, które pozwalają na NULL, wymagają specjalnej obsługi w Go. Standardowy database/sql pakiet zapewnia typy nullable do tego celu.

SQL. Typy zerowe

Użyj sql.NullString, sql.NullInt64, sql.NullFloat64, sql.NullBool, oraz sql.NullTime do obsługi kolumn, które mogą być NULL. Dla wartości daty, godziny i datetime2 w SQL Server, które chcesz oddzielić od domyślnych ustawień Gotime.Time, użyj mssql.NullDate, mssql.NullTime, oraz 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)
}

Obsługa NULL za pomocą wskaźników

Zamiast sql.Null typów, używaj wskaźników. nil Wskaźnik oznacza 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")
}

Wskazówka

Obsługa NULL za pomocą wskaźników jest bardziej zwięzła, ale sql.Null typy sprawiają, że intencja NULL jest bardziej wyraźna w definicjach struktur. Wybierz wzorzec, który preferuje Twój zespół i stosuj go konsekwentnie.

Wyślij wartości NULL jako parametry

Przejdź, nil aby wysłać wartość NULL jako parametr:

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

Precyzja dziesiętna i numeryczna

SQL Server decimal i typy obsługują numeric do 38 cyfr precyzyjności. Go float64 podaje tylko około 15-16 cyfr znaczących. Skanowanie wysokoprecyzyjnych wartości powoduje float64 cichą utratę precyzji.

Skanowanie do łańcucha

Najbezpieczniejszym i najprostszym rozwiązaniem jest zeskanowanie decimal/numeric/money/smallmoney kolumn do string, a następnie konwersja za pomocą precyzyjnie bezpiecznej biblioteki:

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"

Dla ścieżek zależnych od wydajności możesz skanować i []byte analizować bezpośrednio z biblioteką dziesiętną, aby zmniejszyć alokację ciągów znaków.

Użyj sprężyny/dziesiętnej do arytmetyki

Biblioteka shopspring/decimal oferuje arytmetykę dziesiętną o dowolnej precyzji:

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

Uwaga

Nigdy nie skanuj money ani decimal nie wkraczaj do float64 kolumn, gdy dokładna precyzja ma znaczenie (obliczenia finansowe, waluta, podatek). Użyj skanowania wartości dokładnej (stringlub []byte) z lub shopspring/decimal zamiast cockroachdb/apd tego.

Wyślij wartości dziesiętne jako parametry

Wysyłając wartości dziesiętne jako parametry, używaj string , aby uniknąć utraty precyzji pływającej:

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.

Typy dat i godzin

Czas. Mapowanie czasu

Domyślnie kierowca mapuje Go time.Time na datetimeoffset Gon, co zachowuje przesunięcie strefy czasowej. Używaj typów specyficznych dla sterowników dla innych formatów czasu datowego:

Warto go Typ programu SQL Server Kiedy stosować
time.Time datetimeoffset Domyślne. Używaj, gdy strefa czasowa ma znaczenie.
mssql.DateTime1(t) datetime Kolumny dziedziczonego, które nie obsługują offsetu.
mssql.DateTimeOffset(t) datetimeoffset Wyraźna kontrola offsetu.

Najlepsze praktyki UTC

Czas przechowywania w UTC i konwersja na czas lokalny w warstwie aplikacji:

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

wzorce unikalnych identyfikatorów (GUID)

SQL Server przechowuje uniqueidentifier wartości z pierwszych trzech grup w kolejności bajtów little-endian (znanej również jako mieszany endian lub GUID w kolejności bajtów). Jeśli zeskanujesz kolumnę uniqueidentifier do []byte, surowe bajty nie odpowiadają standardowej reprezentacji ciągów znaków. Zamiast tego używaj mssql.UniqueIdentifier , który automatycznie obsługuje konwersję kolejności bajtów.

Skanuj za pomocą mssql. UniqueIdentifier

Domyślnie uniqueidentifier kolumny skanują do []byte. Zastosowanie mssql.UniqueIdentifier do formatowanych ciągów GUID:

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"

Generuj i wysyłaj GUIDy

Użyj NEWID() na serwerze lub generuj w 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)

Generowanie po stronie klienta z pakietem google/uuid :

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 kontrolna mapowania typów

Scenario Zalecane podejście
Kolumny unieważniające Użyj sql.NullStringtypów , sql.NullInt64, sql.NullFloat64, sql.NullBool, sql.NullTime, lub wskaźników.
Wartości finansowe/dziesiętne Skanuj do string, użyj shopspring/decimal do arytmetyki.
Daty związane ze strefami czasowymi Użyj time.Time (domyślnie mapuje na datetimeoffset ).
Starsze kolumny datetime Używaj przy mssql.DateTime1 wysyłaniu parametrów.
Identyfikatory GUID Skanuj z wyjściem mssql.UniqueIdentifier formatowanym.
Ciągi nie-Unicode Używaj, mssql.VarChar by uniknąć ukrytej nvarchar konwersji.
Kolumny JSON Skanuj do string, unmarshal z encoding/json. Zobacz dane JSON i XML.
Kolumny XML Skanuj do string, unmarshal z encoding/xml. Zobacz dane JSON i XML.