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.
Der go-mssqldb Treiber unterstützt Hochleistungs-Bulk-Insert-Operationen mithilfe der Funktion mssql.CopyIn. Bulk Insert umgeht den normalen Zeilen-für-Zeilen-Pfad INSERT und streamt Daten direkt zum Server mithilfe des TDS-Bulk-Copy-Protokolls.
Wähle Bulk Copy, TVP oder JSON
Verwenden Sie folgende Anleitung, wenn Sie mehrere Zeilen oder komplexe Nutzlasten an den SQL Server senden möchten:
| Wählen... | Wenn es am besten passt | Kompromiss |
|---|---|---|
Massenkopieren mit mssql.CopyIn |
Du brauchst den schnellsten Weg, viele Zeilen in einer Zieltabelle zu laden. | Bester Durchsatz, aber er zielt auf Tabellenlasten und nicht auf gespeicherte Prozedurverträge oder gemischte Nutzlasten ab. |
| Ein tabellenwertiger Parameter | Man muss eine stark typisierte Zeilenmenge in eine gespeicherte Prozedur oder einen parametrisierten Befehl übergeben. | Bewahrt Schema- und Prozedurgrenzen, erfordert jedoch einen benutzerdefinierten Tabellentyp und eine passende Feldreihenfolge. |
JSON mit OPENJSON oder FOR JSON |
Deine Anwendung tauscht bereits JSON aus, oder die Struktur der Nutzlast ist verschachtelt oder flexibel. | Portabler für App-Code, aber meist langsamer und weniger typsicher als TVPs oder Massenkopien für strukturierte Einsätze. |
Wenn Sie große Datenmengen in eine Staging-Tabelle oder Zieltabelle laden, beginnen Sie mit dem Massenkopieren. Wenn du gespeicherte Prozeduren mit strukturierten Reihensätzen aufrufst, fang mit TVPs an. Wenn du verschachtelte Dokumente oder lose Schemata brauchst, fang mit JSON an.
Beispiele in diesem Artikel laufen gegen die AdventureWorks2025-Beispieldatenbank . Massenkopie von Beispielen zielt auf HumanResources.Department und Production.ProductCategory.
Einfaches Bulk-Insert
Verwenden Sie mssql.CopyIn, um eine Bulk Copy-Anweisung zu erstellen, und verwenden Sie dann Exec, um Zeilen zu senden:
import (
"database/sql"
"log"
"github.com/microsoft/go-mssqldb"
)
func bulkInsert(db *sql.DB) error {
txn, err := db.Begin()
if err != nil {
return err
}
defer txn.Rollback()
stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department", mssql.BulkOptions{},
"Name", "GroupName"))
if err != nil {
return err
}
// Add rows
_, err = stmt.Exec("Data Science", "Research and Development")
if err != nil {
return err
}
_, err = stmt.Exec("Cloud Ops", "Information Technology")
if err != nil {
return err
}
_, err = stmt.Exec("Developer Relations", "Sales and Marketing")
if err != nil {
return err
}
// Flush and finalize the bulk copy
result, err := stmt.Exec()
if err != nil {
return err
}
if err = stmt.Close(); err != nil {
return err
}
rowsAffected, _ := result.RowsAffected()
log.Printf("Bulk inserted %d rows\n", rowsAffected)
return txn.Commit()
}
Der letzte stmt.Exec() Aufruf ohne Argumente löscht alle verbleibenden Zeilen und schließt die Massenkopierung ab.
Massenoptionen
Die Struktur mssql.BulkOptions konfiguriert das Bulk-Kopierverhalten:
| Feld | Typ | Description |
|---|---|---|
CheckConstraints |
bool |
Überprüfe die Einschränkungen während des Bulk-Einsatzes. |
FireTriggers |
bool |
Lösen Sie Trigger INSERT in der Zieltabelle aus. |
KeepNulls |
bool |
Nullwerte bewahren, anstatt Standardwerte einzufügen. |
KilobytesPerBatch |
int |
Kilobyte pro Batch.
0 Verwendet standardmäßig den Server. |
RowsPerBatch |
int |
Reihen pro Charge.
0 Verwendet standardmäßig den Server. |
Order |
[]string |
ORDER Hinweis für den Ziel-Clusterindex (zum Beispiel []string{"Id ASC"}). |
Tablock |
bool |
Fordern Sie für die Dauer des Massenkopiervorgangs eine Sperre auf Tabellenebene an. |
Beispiel mit Optionen
BulkOptions übergeben, um Constraint-Prüfungen, Trigger und Sperren zu steuern:
stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department",
mssql.BulkOptions{
CheckConstraints: true,
FireTriggers: true,
Tablock: true,
RowsPerBatch: 1000,
},
"Name", "GroupName"))
Fehlerbehandlung
Wenn eine Zeile fehlschlägt, schlägt die gesamte Massenkopie-Operation fehl. Überprüfen Sie Fehler sowohl bei jedem Aufruf von Exec in den einzelnen Zeilen als auch beim abschließenden Flush Exec:
for _, emp := range employees {
_, err = stmt.Exec(emp.Name, emp.GroupName)
if err != nil {
txn.Rollback()
return err
}
}
// Final flush
_, err = stmt.Exec()
if err != nil {
txn.Rollback()
return err
}
Fehlgeschlagene Zeilen erkennen und protokollieren
Wenn eine Massenkopie fehlschlägt, zeigt die Fehlermeldung vom SQL Server die Einschränkung oder das Datenproblem an, identifiziert aber nicht die spezifische Zeile. Um fehlgeschlagene Zeilen zu identifizieren, verwenden Sie einen Batching-Ansatz:
func bulkInsertWithRowTracking(db *sql.DB, departments []Department) error {
txn, err := db.Begin()
if err != nil {
return err
}
stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department",
mssql.BulkOptions{RowsPerBatch: 500}, "Name", "GroupName"))
if err != nil {
txn.Rollback()
return err
}
for i, dept := range departments {
_, err = stmt.Exec(dept.Name, dept.GroupName)
if err != nil {
txn.Rollback()
log.Printf("Bulk copy failed at row %d (Name=%q): %v", i, dept.Name, err)
return fmt.Errorf("bulk copy failed at row %d: %w", i, err)
}
}
_, err = stmt.Exec()
if err != nil {
txn.Rollback()
return fmt.Errorf("bulk copy flush failed: %w", err)
}
if err = stmt.Close(); err != nil {
txn.Rollback()
return err
}
return txn.Commit()
}
Tip
Wenn Sie fehlerhafte Zeilen überspringen und den Vorgang fortsetzen müssen, verwenden Sie einzelne INSERT-Anweisungen oder das Muster mit einer Staging-Tabelle: Führen Sie einen Massenimport in eine Staging-Tabelle ohne Einschränkungen durch und verwenden Sie dann ein MERGE oder INSERT...SELECT mit Fehlerbehandlung, um die Daten in die Zieltabelle zu verschieben.
Stream aus CSV-Dateien
Bei großen CSV-Dateien werden Zeilen direkt aus der Datei in Massenkopien übertragen, ohne die gesamte Datei in den Speicher zu laden:
import (
"encoding/csv"
"io"
"os"
)
func bulkInsertFromCSV(db *sql.DB, filePath string) error {
f, err := os.Open(filePath)
if err != nil {
return err
}
defer f.Close()
reader := csv.NewReader(f)
// Skip the header row.
_, err = reader.Read()
if err != nil {
return err
}
txn, err := db.Begin()
if err != nil {
return err
}
stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department",
mssql.BulkOptions{Tablock: true, RowsPerBatch: 5000},
"Name", "GroupName"))
if err != nil {
txn.Rollback()
return err
}
var rowCount int
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
txn.Rollback()
return fmt.Errorf("CSV read error at row %d: %w", rowCount+1, err)
}
_, err = stmt.Exec(record[0], record[1])
if err != nil {
txn.Rollback()
return fmt.Errorf("row %d: %w", rowCount+1, err)
}
rowCount++
}
// Flush remaining rows.
result, err := stmt.Exec()
if err != nil {
txn.Rollback()
return err
}
if err = stmt.Close(); err != nil {
txn.Rollback()
return err
}
affected, _ := result.RowsAffected()
log.Printf("Bulk inserted %d rows from CSV", affected)
return txn.Commit()
}
Leistungsvergleich
Massenkopien sind bei großen Datenmengen deutlich schneller als einzelne Einsätze. Die folgende Tabelle zeigt ungefähre Leistungsmerkmale für das Einfügen von 100.000 Zeilen:
| Methode | Relative Geschwindigkeit | Netzwerk-Roundtrips | Sperren |
|---|---|---|---|
Individuell INSERT |
Langsamster (1x) | 100,000 | Auf Zeilenebene pro Einfügevorgang. |
Batched INSERT (1.000 Zeilen pro Aussage) |
Mittel (5-10x) | 100 | Reihenebene pro Charge. |
Massenkopieren ohne TABLOCK |
Schnell (20-50x) | Das hängt von der Chargengröße ab | Massenverarbeitung auf Zeilenebene. |
Massenkopieren mit TABLOCK |
Schnellster (50-100x) | Das hängt von der Chargengröße ab | Sperre auf Tabellenebene, minimale Protokollierung. |
Hinweis
Die tatsächliche Leistung variiert je nach Netzwerklatenz, Serverkonfiguration, Tabellenindizes und ob minimales Logging verfügbar ist. Führen Sie mit Ihrer spezifischen Workload einen Benchmark unter Verwendung von testing.B durch. Siehe Leistungsoptimierung.
Spaltenreihenfolge und Typzuordnung
Spalten in mssql.CopyIn müssen der Reihenfolge und den Typen entsprechen, die von der Zieltabelle erwartet werden. Der Treiber führt keinen Abgleich von Spaltennamen durch; er verwendet eine positionsbasierte Zuordnung.
Häufige Typprobleme
| Go-Typ | SQL Server-Spalte | Issue | Lösung |
|---|---|---|---|
string |
varchar |
Implizite nvarchar Konvertierung. |
Verwende den mssql.VarChar Wrapper. |
float64 |
decimal(18,4) |
Präzisionsverlust. | Als string anmelden. |
time.Time |
datetime2 |
Zeitumwandlung. | Nutze UTC-Zeiten. |
nil |
Jede nullierbare Spalte | Erfordert KeepNulls: true. |
Legen Sie KeepNulls in BulkOptionsfest. |
Beispiel mit expliziten Typen
Spezifiziere explizit Spaltentypen, wenn die Standardtypzuordnung nicht mit deinem Schema übereinstimmt:
stmt, err := txn.Prepare(mssql.CopyIn("Production.ProductCategory",
mssql.BulkOptions{KeepNulls: true},
"Name"))
if err != nil {
return err
}
for _, p := range categories {
_, err = stmt.Exec(p.Name)
if err != nil {
return err
}
}
Leistungstipps
-
Verwenden Sie
Tablockfür große Einfügungen bei leeren Tabellen. Diese Option reduziert Sperrkonflikte und ermöglicht eine minimale Protokollierung. -
Set
RowsPerBatchum zu steuern, wie oft der Treiber Daten sendet. Größere Stapel verringern die Anzahl der Hin- und Rückwege, benötigen aber mehr Speicher. -
Erhöhen Sie
packet sizein der Verbindungszeichenfolge (bis auf 32767), um den Netzwerk-Overhead zu reduzieren. -
Ordne die Daten so, dass sie mit dem Cluster-Index der Zieltabelle übereinstimmen, und setze die
OrderOption. Dieser Ansatz vermeidet eine serverseitige Sortierung. - Nicht gruppierte Indizes löschen Sie vor großen Massenladevorgängen und erstellen Sie sie anschließend neu. Die Indexwartung während des Masseneinfügens verursacht zusätzlichen Aufwand.
-
Verwenden Sie
CheckConstraints: false(die Standardeinstellung) für vertrauenswürdige Daten, um die Einschränkungsprüfung während der Massenkopie zu überspringen.
Einschränkungen
- Massenkopien unterstützen keine Spalten, die durch Always Encrypted geschützt sind. Weitere Informationen finden Sie unter "Einschränkungen".
- Bulk-Copy auf TDS-Ebene wird in Azure SQL-Datenbank nicht unterstützt. Für Azure SQL-Datenbank verwenden Sie stattdessen Batch-Anweisungen
INSERToder ein Staging Table Pattern.