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.
Il driver go-mssqldb supporta operazioni di inserimento in blocco ad alte prestazioni utilizzando la funzione mssql.CopyIn. L'inserimento in massa bypassa il normale percorso riga per riga INSERT e trasmette i dati direttamente al server utilizzando il protocollo TDS di copia in massa.
Scegli copia di massa, TVP o JSON
Usa la seguente guida quando devi inviare più righe o payload complessi a SQL Server:
| Scegliere... | Quando si adatta meglio | Compromesso |
|---|---|---|
Copia di massa con mssql.CopyIn |
Hai bisogno del modo più veloce per caricare molte righe in una sola tabella target. | Velocità effettiva migliore, ma è pensato per i caricamenti di tabelle piuttosto che per i contratti delle procedure archiviate o per payload con struttura mista. |
| Un parametro a valori di tabella | Devi passare un insieme di righe fortemente tipizzate in una procedura memorizzata o in un comando parametrizzato. | Preserva i confini di schema e procedura, ma richiede un tipo di tabella definito dall'utente e un ordine di campo corrispondente. |
JSON con OPENJSON o FOR JSON |
La tua applicazione scambia già JSON, oppure la forma del payload è annidata o flessibile. | Più portatile per il codice dell'app, ma di solito più lento e meno sicuro per la tipografia rispetto ai TVP o alle copie in blocco per inserti strutturati. |
Se carichi batch di grandi dimensioni in una tabella di staging o di destinazione, inizia con la copia bulk. Se stai chiamando procedure archiviate con set di righe strutturati, inizia dai TVP. Se hai bisogno di documenti annidati o schemi sparsi, inizia con JSON.
Gli esempi in questo articolo vengono confrontati con il database di esempio AdventureWorks2025 . Esempi di copia in blocco target HumanResources.Department e Production.ProductCategory.
Inserimento in blocco di base
Usare mssql.CopyIn per creare un'istruzione copia in massa, e poi usare Exec per inviare righe:
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()
}
L'ultima stmt.Exec() chiamata senza argomentazioni elimina tutte le righe rimanenti e completa l'operazione di copia in massa.
BulkOptions
La mssql.BulkOptions struct configura il comportamento delle copie di massa:
| Campo | Tipo | Description |
|---|---|---|
CheckConstraints |
bool |
Verifica i vincoli durante l'inserimento in blocco. |
FireTriggers |
bool |
Attiva INSERT trigger sulla tabella di destinazione. |
KeepNulls |
bool |
Preservare i valori nulli invece di inserire valori predefiniti. |
KilobytesPerBatch |
int |
Kilobyte per batch.
0 Usa il server predefinito. |
RowsPerBatch |
int |
Righe per blocco.
0 Usa il server predefinito. |
Order |
[]string |
ORDER il suggerimento per l'indice cluster di destinazione (ad esempio, []string{"Id ASC"}). |
Tablock |
bool |
Acquisisci un blocco a livello di tabella per tutta la durata della copia in massa. |
Esempio con opzioni
Passare BulkOptions per controllare i controlli dei vincoli, i trigger e il blocco:
stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department",
mssql.BulkOptions{
CheckConstraints: true,
FireTriggers: true,
Tablock: true,
RowsPerBatch: 1000,
},
"Name", "GroupName"))
Gestione degli errori
Se una riga fallisce, l'intera operazione di copia in blocco fallisce. Controlla gli errori sia dalla chiamata Exec di ogni riga che dal flush finale 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
}
Rilevare e registrare le righe fallite
Quando un'operazione di copia in massa fallisce, il messaggio di errore di SQL Server indica il vincolo o il problema dei dati ma non identifica la riga specifica. Per identificare le righe non riuscite, utilizza un approccio per batch:
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
Se devi saltare righe non valide e continuare, usa istruzioni individuali INSERT o un modello con tabella di staging: esegui una copia bulk in una tabella di staging senza vincoli, quindi usa un'istruzione MERGE o INSERT...SELECT con gestione degli errori per spostare i dati nella tabella di destinazione.
Streaming da file CSV
Per file CSV di grandi dimensioni, trasmetti le righe direttamente dal file in copia in massa senza caricare l'intero file in memoria:
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()
}
Confronto delle prestazioni
La copia di massa è significativamente più veloce rispetto agli inserti individuali per carichi dati elevati. La tabella seguente mostra le caratteristiche di prestazione approssimative per l'inserimento di 100.000 righe:
| metodo | Velocità relativa | Viaggi di andata e ritorno della rete | Bloccaggio |
|---|---|---|---|
Singolo INSERT |
Più lento (1x) | 100,000 | A livello di riga per inserto. |
In batch INSERT (1.000 righe per istruzione) |
Media (5-10 volte) | 100 | A livello di riga per lotto. |
Copia in blocco senza TABLOCK |
Veloce (20-50x) | Dipende dalla dimensione del lotto | Volume a livello di fila. |
Copia di massa con TABLOCK |
Il più veloce (50-100x) | Dipende dalla dimensione del lotto | Blocco a livello di tabella, registrazione minima. |
Note
Le prestazioni effettive variano in base alla latenza di rete, alla configurazione del server, agli indici delle tabelle e alla disponibilità di logging minimo. Fai un benchmark con il tuo carico di lavoro specifico usando testing.B. Vedere Ottimizzazione delle prestazioni.
Ordinamento delle colonne e mappatura dei tipi
Le colonne in mssql.CopyIn devono corrispondere all'ordine e ai tipi attesi dalla tabella target. Il driver non esegue il matching dei nomi delle colonne; Utilizza la mappatura posizionale.
Problemi di tipo comune
| tipo Go | Colonna SQL Server | Issue | Soluzione |
|---|---|---|---|
string |
varchar |
Conversione implicita nvarchar. |
Usa mssql.VarChar wrapper. |
float64 |
decimal(18,4) |
Perdita di precisione. | Passa per string. |
time.Time |
datetime2 |
Conversione fuso orario. | Usa gli orari UTC. |
nil |
Qualsiasi colonna nullabile | Richiede KeepNulls: true. |
Impostare KeepNulls in BulkOptions. |
Esempio con tipi espliciti
Specifica esplicitamente i tipi di colonna quando la mappatura predefinita dei tipi non corrisponde al tuo schema:
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
}
}
Suggerimenti per le prestazioni
-
Usa
Tablockper inserimenti di grandi dimensioni in tabelle vuote. Questa opzione riduce la contenzione dei lock e consente una registrazione minima. -
Set
RowsPerBatchper controllare con quale frequenza il driver invia dati. I lotti più grandi riducono i viaggi di andata e ritorno ma usano più memoria. -
Aumentare
packet sizenella stringa di connessione (fino a 32767) per ridurre il sovraccarico di rete. -
Ordina i dati per corrispondere all'indice clusterizzato della tabella target e imposta l'opzione
Order. Questo approccio evita un ordinamento sul server. - Elimina gli indici non clusterizzati prima di carichi grandi in massa, poi ricostruiscili dopo. La manutenzione dell'indice durante l'inserimento in massa aggiunge costi aggiuntivi.
-
Utilizzo
CheckConstraints: false(il valore predefinito) per permettere ai dati affidabili di saltare il controllo dei vincoli durante la copia in massa.
Limitations
- Bulk copy non supporta colonne protette da Always Encrypted. Per altre informazioni, vedere Limitazioni.
- La copia di massa a livello TDS non è supportata su database SQL di Azure. Con database SQL di Azure, usa invece istruzioni in batch
INSERTo un modello con tabella di staging.