Восстанавливаемая массовая загрузка в SQL Server
Разделите исходную загрузку, проверку типов и публикацию, чтобы ошибочные файлы и повторы не оставляли рабочие таблицы заполненными частично.
Быстрая массовая загрузка является лишь частью надежного импорта. Затем нужно объяснить отклоненные строки, распознать уже обработанный файл и не показать пользователям половину набора данных. Промежуточная область staging делает эти задачи явными.
Сохранить источник до преобразования
Назначьте партии стабильный идентификатор и сохраните имя источника, хеш содержимого, время поступления и версию парсера. Одного имени файла недостаточно: поставщик может прислать под ним другие данные. Храните исходные поля и идентификатор записи для поиска ошибок.
Сначала загружайте в изолированную область. BULK INSERT использует путь, доступный хосту SQL Server или настроенному источнику, а не ноутбуку аналитика. Уточните учетную запись доступа и формат. Протестируйте кодировку, пустые поля, разделители внутри кавычек и вложенные переводы строк.
Физическая строка текста не всегда соответствует логической записи CSV. Если нужны точные позиции, их должен сохранять парсер. Значение identity промежуточной таблицы само по себе не доказывает исходный порядок файла.
Пример начинается после разбора файла и показывает переход от исходных строк к типизированным значениям. Это не реализация полноценного CSV-парсера.
DECLARE @Raw TABLE
(
SourceRow int PRIMARY KEY,
CustomerText nvarchar(100),
DateText nvarchar(100),
AmountText nvarchar(100)
);
INSERT @Raw VALUES
(1, N'42', N'20260601', N'125.50'),
(2, N'bad', N'20260602', N'20.00'),
(3, N'43', N'20260230', N'15.00'),
(4, N'44', N'20260603', N''),
(5, N'45', N'20260604', N'-7.00');
SELECT r.*,
TRY_CONVERT(int, NULLIF(LTRIM(RTRIM(CustomerText)), N'')) AS CustomerId,
TRY_CONVERT(date, NULLIF(LTRIM(RTRIM(DateText)), N''), 112) AS InvoiceDate,
TRY_CONVERT(decimal(19,4), NULLIF(LTRIM(RTRIM(AmountText)), N'')) AS Amount
INTO #Parsed
FROM @Raw AS r;
SELECT SourceRow, CustomerId, InvoiceDate, Amount,
CASE
WHEN CustomerId IS NULL OR CustomerId <= 0 THEN N'Invalid customer'
WHEN InvoiceDate IS NULL THEN N'Invalid date'
WHEN Amount IS NULL OR Amount <= 0 THEN N'Invalid amount'
ELSE N'Accepted'
END AS ValidationResult
FROM #Parsed
ORDER BY SourceRow;
DROP TABLE #Parsed;
Принимается только запись 1. У записи 2 неверный клиент, у 3 невозможная дата, у 4 пустая сумма, у 5 отрицательная сумма. NULLIF не позволяет трактовать пустое числовое поле как полезное число. Стиль 112 задает договоренность YYYYMMDD независимо от языка сессии.
Сделать отклонение объяснимым результатом
TRY_CONVERT отделяет многие ошибки преобразования от сбоя всей партии. Но успешное преобразование еще не подтверждает бизнес-корректность. Пример принимает положительные идентификаторы, не проверяя существование клиента. Перед публикацией сопоставьте их с разрешенным набором клиентов.
Преобразование decimal может округлить лишние дробные разряды. Если это запрещено, проверяйте исходную запись или сравнивайте ее с допустимым более точным представлением. Тип целевого столбца не заменяет контракт входных данных.
CASE показывает первую ошибку записи для удобства чтения. Рабочая таблица ошибок может содержать несколько причин: партия, исходная запись, поле, код и оригинальное значение. Назначьте подходящие права и срок хранения. Сообщение только о неудачном импорте не помогает поставщику исправить конкретные данные.
Проверьте дубликаты внутри партии и относительно бизнес-ключа целевой таблицы. Заранее решите, означают ли они отказ, замену или намеренную агрегацию. DISTINCT ради исчезновения ошибки уникальности может скрыть проблему поставщика либо потерять значимое различие.
Сверяйте число исходных записей, ошибок парсинга, отклонений проверки, принятых и опубликованных строк. Определите категории так, чтобы несколько ошибок одной строки не увеличивали число отклоненных записей. Сохраняйте итоговую сверку вместе с партией.
Определить границу публикации и повтора
Выберите полную или допустимую частичную приемку. Для атомарной публикации завершите проверку до короткой транзакции, которая переносит данные и помечает партию опубликованной. Ограничения целевой таблицы остаются необходимыми: справочники могли измениться после предварительной проверки.
Обеспечьте уникальность идентификатора партии или операции. После потери соединения вслед за commit повтор должен прочитать сохраненный результат. Маркер публикации и изменения данных обязаны фиксироваться вместе.
Большие импорты могут потребовать ограниченных частей. Тогда храните завершенные части и обеспечьте безопасный повтор каждой либо скрывайте данные до финального состояния, которое учитывают все читатели. Флаг, забытый одним отчетом, не обеспечивает такую границу.
Измеряйте вместе разбор, проверочные join, рост журнала, обслуживание индексов и публикацию. Храните отклоненные данные для исправления и повторной обработки, затем удаляйте по установленному правилу. Для эксплуатации полезен отдельный сценарий зависшей партии: кто проверяет ее состояние, какой результат считается окончательным и как запускается повтор. Надежность означает объяснимый исход и безопасное продолжение, а не только высокую скорость первой стадии.
Техническая документация: Microsoft Learn: BULK INSERT · Microsoft Learn: TRY_CONVERT.