Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
SQL Server hat integrierte Unterstützung für JSON- und XML-Daten. Dieser Artikel zeigt, wie man den Treiber go-mssqldb verwendet, um JSON- und XML-Daten zwischen Go-Strukturen und SQL Server abzufragen, einzufügen und zu transformieren.
Beispiele in diesem Artikel laufen gegen die AdventureWorks2025-Beispieldatenbank . Leseorientierte Beispiele fragen eingebaute Objekte wie Sales.vSalesPerson und Sales.SalesOrderHeaderab. Schreiborientierte Beispiele verwenden OPENJSON und XML .nodes(), um Zeilen in HumanResources.Department einzufügen.
Die meisten Snippets nehmen ctx an und db sind bereits initialisiert. Du kannst dieses Setup-Muster verwenden:
import (
"context"
"database/sql"
"log"
"time"
_ "github.com/microsoft/go-mssqldb"
)
db, err := sql.Open("sqlserver", "sqlserver://<user>:<password>@<server>?database=AdventureWorks2025&encrypt=true&TrustServerCertificate=true")
if err != nil {
log.Fatal(err)
}
defer db.Close()
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
JSON-Daten
Abfrageergebnisse als JSON mit FOR-JSON abfragen
Verwenden Sie FOR JSON PATH, um Abfrageergebnisse als JSON-String zurückzugeben:
var jsonResult string
err := db.QueryRowContext(ctx,
"SELECT BusinessEntityID AS Id, FirstName + ' ' + LastName AS Name, CountryRegionName AS Department FROM Sales.vSalesPerson WHERE CountryRegionName = @dept FOR JSON PATH",
sql.Named("dept", "United States")).Scan(&jsonResult)
if err != nil {
log.Fatal(err)
}
fmt.Println(jsonResult)
Note
SQL Server kann FOR JSON Ausgabe bei größeren Nutzdatenmengen in mehreren Zeilen zurückgeben. Wenn Sie größere Ergebnisse zuverlässig unterstützen müssen, lesen Sie alle zurückgegebenen Reihen und verbinden Sie sie. Siehe Umgang mit großen JSON-Ergebnissen.
Handhabung großer JSON-Ergebnisse
Wenn SQL Server JSON-Ausgabe in mehreren Zeilen zurückgibt, fügen Sie die Teile vor dem Deserialisieren oder Ausgeben des Ergebnisses zusammen:
import "strings"
func queryJSON(ctx context.Context, db *sql.DB, query string, args ...any) (string, error) {
rows, err := db.QueryContext(ctx, query, args...)
if err != nil {
return "", err
}
defer rows.Close()
var sb strings.Builder
for rows.Next() {
var chunk string
if err := rows.Scan(&chunk); err != nil {
return "", err
}
sb.WriteString(chunk)
}
if err := rows.Err(); err != nil {
return "", err
}
return sb.String(), nil
}
Verwendung:
jsonStr, err := queryJSON(ctx, db,
"SELECT BusinessEntityID AS Id, FirstName + ' ' + LastName AS Name, CountryRegionName AS Department FROM Sales.vSalesPerson FOR JSON PATH")
if err != nil {
log.Fatal(err)
}
Unmarshal JSON führt zu Go-Strukturen
Kombinieren Sie FOR JSON mit encoding/json, um SQL Server-Ergebnisse direkt in Go-Typen zu deserialisieren:
import "encoding/json"
type Employee struct {
ID int `json:"Id"`
Name string `json:"Name"`
Department string `json:"Department"`
}
func getEmployees(ctx context.Context, db *sql.DB, dept string) ([]Employee, error) {
var jsonStr string
err := db.QueryRowContext(ctx,
"SELECT BusinessEntityID AS Id, FirstName + ' ' + LastName AS Name, CountryRegionName AS Department FROM Sales.vSalesPerson WHERE CountryRegionName = @dept FOR JSON PATH",
sql.Named("dept", dept)).Scan(&jsonStr)
if err != nil {
return nil, err
}
var employees []Employee
if err := json.Unmarshal([]byte(jsonStr), &employees); err != nil {
return nil, err
}
return employees, nil
}
Verschachteltes JSON mit FOR JSON PATH
Erstellen Sie verschachtelte JSON-Strukturen durch Verwendung von Punktnotation in Spaltenaliasen:
var jsonResult string
err := db.QueryRowContext(ctx, `
SELECT
soh.SalesOrderID AS [Id],
soh.OrderDate AS [OrderDate],
CONCAT(pp.FirstName, ' ', pp.LastName) AS [Customer.Name],
ea.EmailAddress AS [Customer.Email],
soh.TotalDue AS [Total]
FROM Sales.SalesOrderHeader AS soh
JOIN Sales.Customer AS c ON soh.CustomerID = c.CustomerID
LEFT JOIN Person.Person AS pp ON c.PersonID = pp.BusinessEntityID
LEFT JOIN Person.EmailAddress AS ea ON pp.BusinessEntityID = ea.BusinessEntityID
WHERE soh.SalesOrderID = @id
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER`,
sql.Named("id", 43659)).Scan(&jsonResult)
Das Ergebnis:
{"Id":43659,"OrderDate":"2022-05-30T00:00:00","Customer":{"Name":"James Hendergart","Email":"james9@adventure-works.com"},"Total":23153.2339}
Parse JSON in SQL Server mit OPENJSON
Mit OPENJSON ein JSON-Array serverseitig in Zeilen aufteilen:
jsonData := `[
{"Name": "Alice", "Department": "Engineering"},
{"Name": "Bob", "Department": "Marketing"}
]`
rows, err := db.QueryContext(ctx, `
SELECT Name, Department
FROM OPENJSON(@json)
WITH (
Name NVARCHAR(100) '$.Name',
Department NVARCHAR(100) '$.Department'
)`, sql.Named("json", jsonData))
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var name, dept string
if err := rows.Scan(&name, &dept); err != nil {
log.Fatal(err)
}
fmt.Printf("%s - %s\n", name, dept)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
JSON-Daten als Parameter einfügen
Senden Sie eine Go-Struktur als JSON-String an eine gespeicherte Prozedur oder OPENJSON:
type OrderItem struct {
ProductID int `json:"productId"`
Quantity int `json:"quantity"`
Price float64 `json:"price"`
}
func sendJSONPayload(ctx context.Context, db *sql.DB, metadata map[string]any) error {
jsonBytes, err := json.Marshal(metadata)
if err != nil {
return err
}
// Pass JSON to a stored procedure for server-side processing.
_, err = db.ExecContext(ctx,
"EXECUTE dbo.ProcessDepartmentMetadata @payload",
sql.Named("payload", string(jsonBytes)))
return err
}
Verwendung:
metadata := map[string]any{
"source": "docs-sample",
"batchId": "b-20260709-01",
"departments": []map[string]any{
{"name": "Data Science", "groupName": "Research and Development"},
{"name": "Cloud Ops", "groupName": "Information Technology"},
},
}
if err := sendJSONPayload(ctx, db, metadata); err != nil {
log.Fatal(err)
}
Batch-Insert aus JSON mit OPENJSON
Fügen Sie mehrere Zeilen aus einem JSON-Array in einer einzigen Anweisung ein:
func bulkInsertFromJSON(ctx context.Context, db *sql.DB, jsonData string) (int64, error) {
result, err := db.ExecContext(ctx, `
INSERT INTO HumanResources.Department (Name, GroupName)
SELECT Name, GroupName
FROM OPENJSON(@json)
WITH (
Name NVARCHAR(50) '$.name',
GroupName NVARCHAR(50) '$.groupName'
)`, sql.Named("json", jsonData))
if err != nil {
return 0, err
}
return result.RowsAffected()
}
Verwendung:
jsonData := `[
{"name": "Data Science", "groupName": "Research and Development"},
{"name": "Cloud Ops", "groupName": "Information Technology"},
{"name": "Developer Relations", "groupName": "Sales and Marketing"}
]`
rowsInserted, err := bulkInsertFromJSON(ctx, db, jsonData)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Inserted %d rows\n", rowsInserted)
JSON_VALUE und JSON_QUERY
Werte aus JSON-Spalten extrahieren, ohne das gesamte JSON-Dokument zu übertragen:
// Extract a scalar value.
var productName string
err := db.QueryRowContext(ctx, `
SELECT Name
FROM Production.Product
WHERE ProductID = @id`,
sql.Named("id", 1)).Scan(&productName)
if err != nil {
log.Fatal(err)
}
fmt.Println(productName)
// Extract a JSON array from a FOR JSON subquery.
var itemsJSON string
err = db.QueryRowContext(ctx, `
SELECT (
SELECT ProductID, Name
FROM Production.Product
WHERE ProductSubcategoryID = @subId
FOR JSON PATH
) AS Items`,
sql.Named("subId", 1)).Scan(&itemsJSON)
if err != nil {
log.Fatal(err)
}
fmt.Println(itemsJSON)
Tip
JSON_VALUE gibt einen skalaren Wert (Zeichenkette, Zahl) zurück.
JSON_QUERY gibt ein Objekt oder Array zurück. Verwenden Sie die richtige Funktion für den benötigten Datentyp.
JSON-berechnete Spalten und Indizes
Für häufig abgefragte JSON-Eigenschaften erstellen Sie berechnete Spalten mit Indizes für bessere Leistung:
-- Demo table with JSON-only rows.
IF OBJECT_ID('dbo.ProductJsonDemo', 'U') IS NOT NULL
DROP TABLE dbo.ProductJsonDemo;
CREATE TABLE dbo.ProductJsonDemo (
ProductDescriptionID INT IDENTITY(1,1) PRIMARY KEY,
Metadata NVARCHAR(MAX) NOT NULL,
CONSTRAINT CK_ProductJsonDemo_Metadata_IsJson CHECK (ISJSON(Metadata) = 1),
-- Cast to a bounded length so the index key stays under SQL Server limits.
LocaleName AS CAST(JSON_VALUE(Metadata, '$.locale') AS NVARCHAR(10)) PERSISTED
);
INSERT INTO dbo.ProductJsonDemo (Metadata)
VALUES
(N'{"locale":"en","title":"Road helmet"}'),
(N'{"locale":"fr","title":"Casque de route"}');
CREATE INDEX IX_ProductJsonDemo_LocaleName ON dbo.ProductJsonDemo(LocaleName);
Dann fragen Sie die berechnete Spalte direkt von Go ab:
rows, err := db.QueryContext(ctx,
"SELECT ProductDescriptionID, LocaleName FROM dbo.ProductJsonDemo WHERE LocaleName = @name",
sql.Named("name", "en"))
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var localeName string
if err := rows.Scan(&id, &localeName); err != nil {
log.Fatal(err)
}
fmt.Printf("%d %s\n", id, localeName)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
XML-Daten
Abfrage der Ergebnisse als XML mit FOR XML
Verwenden Sie FOR XML PATH, um Abfrageergebnisse als XML zurückzugeben:
var xmlResult string
err := db.QueryRowContext(ctx, `
SELECT BusinessEntityID AS [@id], FirstName + ' ' + LastName AS Name, CountryRegionName AS Department
FROM Sales.vSalesPerson
WHERE CountryRegionName = @dept
FOR XML PATH('Employee'), ROOT('Employees')`,
sql.Named("dept", "United States")).Scan(&xmlResult)
if err != nil {
log.Fatal(err)
}
fmt.Println(xmlResult)
Das Ergebnis:
<Employees>
<Employee id="274"><Name>Stephen Jiang</Name><Department>United States</Department></Employee>
<Employee id="275"><Name>Michael Blythe</Name><Department>United States</Department></Employee>
<Employee id="276"><Name>Linda Mitchell</Name><Department>United States</Department></Employee>
<Employee id="277"><Name>Jillian Carson</Name><Department>United States</Department></Employee>
<Employee id="279"><Name>Tsvi Reiter</Name><Department>United States</Department></Employee>
<Employee id="280"><Name>Pamela Ansman-Wolfe</Name><Department>United States</Department></Employee>
<Employee id="281"><Name>Shu Ito</Name><Department>United States</Department></Employee>
<Employee id="283"><Name>David Campbell</Name><Department>United States</Department></Employee>
<Employee id="284"><Name>Tete Mensa-Annan</Name><Department>United States</Department></Employee>
<Employee id="285"><Name>Syed Abbas</Name><Department>United States</Department></Employee>
<Employee id="287"><Name>Amy Alberts</Name><Department>United States</Department></Employee>
</Employees>
Umgang mit großen XML-Ergebnissen
Wie bei JSON werden große XML-Ergebnisse auf Zeilen verteilt.
func queryXML(ctx context.Context, db *sql.DB, query string, args ...any) (string, error) {
rows, err := db.QueryContext(ctx, query, args...)
if err != nil {
return "", err
}
defer rows.Close()
var sb strings.Builder
for rows.Next() {
var chunk string
if err := rows.Scan(&chunk); err != nil {
return "", err
}
sb.WriteString(chunk)
}
if err := rows.Err(); err != nil {
return "", err
}
return sb.String(), nil
}
XML-Ergebnisse in Go parsen
Verwenden Sie das Paket encoding/xml, um XML-Ergebnisse zu deserialisieren.
import "encoding/xml"
type EmployeeList struct {
XMLName xml.Name `xml:"Employees"`
Employees []Employee `xml:"Employee"`
}
type Employee struct {
ID int `xml:"id,attr"`
Name string `xml:"Name"`
Department string `xml:"Department"`
}
func getEmployeesXML(ctx context.Context, db *sql.DB, dept string) (*EmployeeList, error) {
xmlStr, err := queryXML(ctx, db, `
SELECT BusinessEntityID AS [@id], FirstName + ' ' + LastName AS Name, CountryRegionName AS Department
FROM Sales.vSalesPerson
WHERE CountryRegionName = @dept
FOR XML PATH('Employee'), ROOT('Employees')`,
sql.Named("dept", dept))
if err != nil {
return nil, err
}
var result EmployeeList
if err := xml.Unmarshal([]byte(xmlStr), &result); err != nil {
return nil, err
}
return &result, nil
}
XML-Parameter an SQL Server weitergeben
Senden Sie ein XML-Dokument an ein gespeichertes Verfahren oder eine Abfrage:
xmlData := `<Employees>
<Employee><Name>Alice</Name><Department>Engineering</Department></Employee>
<Employee><Name>Bob</Name><Department>Marketing</Department></Employee>
</Employees>`
rows, err := db.QueryContext(ctx, `
DECLARE @xmlDoc XML = CAST(@xml AS XML);
SELECT
e.value('(Name)[1]', 'NVARCHAR(100)') AS Name,
e.value('(Department)[1]', 'NVARCHAR(100)') AS Department
FROM @xmlDoc.nodes('/Employees/Employee') AS t(e)`,
sql.Named("xml", xmlData))
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var name, dept string
if err := rows.Scan(&name, &dept); err != nil {
log.Fatal(err)
}
fmt.Printf("%s - %s\n", name, dept)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
Note
Der Parameter @xml wird standardmäßig als nvarchar(max) gesendet. Die Methode .nodes() benötigt den XML-Datentyp , daher wird der Parameter explizit mit CAST(@xml AS XML)gegeben.
Zeilen aus XML einfügen
Verwenden Sie die Methode .nodes() mit einer INSERT...SELECT Anweisung, um XML in Tabellenzeilen zu zerschneiden.
func bulkInsertFromXML(ctx context.Context, db *sql.DB, xmlData string) (int64, error) {
result, err := db.ExecContext(ctx, `
DECLARE @xmlDoc XML = CAST(@xml AS XML);
INSERT INTO HumanResources.Department (Name, GroupName)
SELECT
e.value('(Name)[1]', 'NVARCHAR(50)'),
e.value('(GroupName)[1]', 'NVARCHAR(50)')
FROM @xmlDoc.nodes('/Departments/Department') AS t(e)`,
sql.Named("xml", xmlData))
if err != nil {
return 0, err
}
return result.RowsAffected()
}
Wählen Sie zwischen JSON und XML
| Consideration | JSON | XML |
|---|---|---|
| Unterstützung des Go-Ökosystems | Standard encoding/json. Struct-Tags zur Zuordnung. |
Standard encoding/xml. Ausführlichere Struct-Tags. |
| SQL Server-Unterstützung |
OPENJSON, JSON_VALUE, JSON_QUERY, ( FOR JSON SQL Server 2016 und spätere Versionen) |
nodes(), value(), query(), ( FOR XML alle Versionen) |
| Leistung | Im Allgemeinen schnelleres Parsen. Weniger ausführliches Drahtformat. | Unterstützt Schemata und Validierung. Wortreicher. |
| Schemavalidierung | Keine integrierte Schema-Validierung im SQL Server. | Unterstützt XML Schema Collections für serverseitige Validierung. |
| Indizierung | Berechnete Spalten mit JSON_VALUE + Index. | XML-Indizes (primär und sekundär). |
| Datenmodellierung | Arrays und verschachtelte Objekte. Natürliche Passform für Go-Slices und Maps. | Hierarchische Dokumente mit Attributen und Namensräumen. |
Tip
Für Neuentwicklungen ist JSON in der Regel die bessere Wahl. Es erfordert weniger Overhead beim Parsen, erzeugt kleinere Nutzdaten und lässt sich auf natürliche Weise auf Go-Structs abbilden. Verwenden Sie XML, wenn Sie eine Schema-Validierung benötigen oder bei der Integration mit Systemen, die XML benötigen.