Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Драйвер go-mssqldb поддерживает высокопроизводительные операции массовой вставки с помощью функции mssql.CopyIn. Bulk Insert обходит обычный путь по INSERT строкам и направляет данные непосредственно на сервер с помощью протокола массового копирования TDS.
Выбирайте массовое копиирование, TVP или JSON
Используйте следующую инструкцию, если вам нужно отправить несколько строк или сложные наборы данных в SQL Server:
| Выбрать... | Когда это подходит лучше всего | Компромиссы |
|---|---|---|
Массовое копирование с mssql.CopyIn |
Вам нужен самый быстрый способ загрузить множество строк в одну целевой таблицу. | Наилучшая пропускная способность, но ориентировано это скорее на загрузку таблиц, а не на контракты хранимых процедур или полезные нагрузки смешанной структуры. |
| Параметр с табличным значением | Нужно передать сильно типизированный набор строк в сохранённую процедуру или параметризованную команду. | Сохраняет границы схемы и процедур, но требует пользовательского типа таблицы и соответствующего порядка полей. |
JSON с OPENJSON или FOR JSON |
Ваше приложение уже обменивается данными в формате JSON, или полезная нагрузка имеет вложенную либо гибкую структуру. | Более портативный для кода приложения, но обычно медленнее и менее безопасно для шрифта, чем TVP или массовые копии для структурированных вставок. |
Если вы загружаете большие пакеты данных в промежуточную или целевую таблицу, начните с пакетной загрузки. Если вы вызываете хранящиеся процедуры со структурированными наборами рядов, начинайте с TVP. Если нужны вложенные документы или размытые схемы, начните с JSON.
Примеры в этой статье выполняются в образце базы данных AdventureWorks2025. Примеры массового копирования предназначены для HumanResources.Department и Production.ProductCategory.
Базовая массовая вставка
Используйте mssql.CopyIn для создания инструкции bulk copy, а затем используйте Exec для отправки строк:
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()
}
Последний stmt.Exec() вызов без аргументов очищает все оставшиеся строки и завершает операцию массового копирования.
BulkOptions
Структура mssql.BulkOptions настраивает поведение массового копирования:
| Поле | Type | Description |
|---|---|---|
CheckConstraints |
bool |
Проверять ограничения при пакетной вставке. |
FireTriggers |
bool |
Запускайте INSERT триггеры на столе цели. |
KeepNulls |
bool |
Сохраняйте значения null вместо подстановки значений по умолчанию. |
KilobytesPerBatch |
int |
Килобайты на партию.
0 использует значение сервера по умолчанию. |
RowsPerBatch |
int |
Ряды на партию.
0 использует значение сервера по умолчанию. |
Order |
[]string |
ORDER подсказка для целевого кластерного индекса (например, []string{"Id ASC"}). |
Tablock |
bool |
Установить блокировку на уровне таблицы на время выполнения операции массового копирования. |
Пример с опциями
Передайте BulkOptions, чтобы управлять проверками ограничений, триггерами и блокировкой:
stmt, err := txn.Prepare(mssql.CopyIn("HumanResources.Department",
mssql.BulkOptions{
CheckConstraints: true,
FireTriggers: true,
Tablock: true,
RowsPerBatch: 1000,
},
"Name", "GroupName"))
Обработка ошибок
Если хотя бы одна строка завершается с ошибкой, вся операция массового копирования завершается с ошибкой. Проверяйте ошибки как при вызове Exec для каждой строки, так и при окончательном сбросе 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
}
Обнаружение и запись неудачных строк
Когда операция массового копирования не удаётся, сообщение об ошибке от SQL Server указывает на ограничение или проблему с данными, но не указывает конкретную строку. Для выявления неисправных строк используйте пакетный подход:
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
Если нужно пропустить ошибочные строки и продолжить, используйте отдельные операторы INSERT или шаблон с промежуточной таблицей: выполните массовое копирование в промежуточную таблицу без ограничений, затем используйте MERGE или INSERT...SELECT с обработкой ошибок, чтобы перенести данные в целевую таблицу.
Поток из CSV-файлов
Для больших CSV-файлов передавайте строки напрямую из файла в bulk copy, не загружая весь файл в память:
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()
}
Сравнение производительности
Массовое копирование значительно быстрее, чем отдельные вставки для больших загрузок данных. В следующей таблице показаны приблизительные характеристики для вставки 100 000 строк:
| Метод | Относительная скорость | Маршруты по сети туда и обратно | Блокировка |
|---|---|---|---|
Индивидуальный INSERT |
Самый медленный (1 раз) | 100,000 | Уровень строки для каждой вставки. |
Пакетная обработка INSERT (1 000 строк в одном выражении) |
Средний (5–10 раз) | 100 | На уровне строки для каждого пакета. |
Массовое копирование без TABLOCK |
Быстро (20–50 раз) | Зависит от размера партии | Пакетная обработка на уровне строк. |
Массовое копирование с TABLOCK |
Самый быстрый (50-100 раз) | Зависит от размера партии | Блокировка на уровне таблицы, минимальное журналирование. |
Замечание
Фактическая производительность зависит от сетевой задержки, конфигурации сервера, индексов таблиц и возможности минимального логирования. Проводите бенчмарк с вашей конкретной нагрузкой с помощью testing.B. См. раздел "Настройка производительности".
Упорядочение столбцов и отображение типов
Столбцы в mssql.CopyIn должны соответствовать порядку и типам данных, требуемым целевой таблицей. Драйвер не выполняет сопоставление имён столбцов; Он использует позиционное отображение.
Распространённые типографические проблемы
| тип Go | Столбец SQL Server | Issue | Solution |
|---|---|---|---|
string |
varchar |
Неявное nvarchar преобразование. |
Используйте mssql.VarChar обёртку. |
float64 |
decimal(18,4) |
Потеря точности. | Пройти как string. |
time.Time |
datetime2 |
Преобразование часовых поясов. | Используйте время UTC. |
nil |
Любой нулируемый столбец | Требует использования KeepNulls: true. |
Установите KeepNulls в BulkOptions. |
Пример с явными типами
Указывайте типы столбцов явно, если стандартное отображение типов не совпадает с вашей схемой:
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
}
}
Советы по производительности
-
Используйте
Tablockдля массовой вставки в пустые таблицы. Этот параметр снижает конкуренцию за блокировки и позволяет использовать минимальное журналирование. -
Установите
RowsPerBatch, чтобы управлять тем, как часто драйвер отправляет данные. Большие партии сокращают круговые поездки, но требуют больше памяти. -
Увеличьте
packet sizeв строке подключения (до 32767), чтобы снизить сетевые накладные расходы. -
Упорядочивайте данные так, чтобы они совпадали с кластерным индексом целевой таблицы, и задайте эту
Orderопцию. Этот подход избегает сортировки на стороне сервера. - Убрать некластерные индексы перед крупными массовыми загрузками, а потом восстановить их после. Обслуживание индекса во время массовой вставки создаёт дополнительные накладные расходы.
-
Используйте
CheckConstraints: false(по умолчанию) для доверенных данных, чтобы пропустить проверку ограничений при массовом копировании.
Ограничения
- Массовое копирование не поддерживает колонки, защищённые Always Encrypted. Дополнительные сведения см. в статье Ограничения.
- Массовое копирование уровня TDS не поддерживается в База данных SQL Azure. Для База данных SQL Azure вместо этого используйте операторы
INSERT, выполняемые пакетами, или подход с промежуточной таблицей.