Потребность фиксировать ошибочные строки при массовом импортировании возникла практически сразу после появления автоматических загрузок больших объемов данных в базы. Традиционно, любое нарушение ограничений (например, нарушения типов данных, констрейнтов) приводило к прерыванию всего процесса импорта даже ради одной ошибочной строки — в результате терялись и корректные, и некорректные записи.
Проблема состоит в том, чтобы не только загрузить максимум корректных данных, но и оставить аудит и подробное описание ошибок для дальнейшей ручной доработки, а не просто "откатить всё".
Решение: Для некоторых СУБД (например, PostgreSQL, SQL Server с SSIS, Oracle с утилитами EXTERNAL TABLES) существуют механизмы загрузки с разделением bad rows. Для универсального способа — используют staging-таблицы и post-upload-валидацию через OUTER JOIN и NOT EXISTS, а также присоединённые "лог-таблицы", куда вручную пишутся проблемные строки.
Пример кода:
-- 1. Загружаем всё во временную staging-таблицу BULK INSERT staging_payments FROM 'payments.csv' WITH (...) -- 2. Фильтруем корректные INSERT INTO payments (id, amount, ...) SELECT id, amount, ... FROM staging_payments WHERE amount > 0 AND status IN ('approved', 'pending'); -- 3. Логируем ошибочные строки в отдельную таблицу INSERT INTO import_errors (row_data, error_desc) SELECT *, 'Некорректный статус или отрицательная сумма' FROM staging_payments WHERE amount <= 0 OR status NOT IN ('approved', 'pending');
Ключевые особенности:
Можно ли реализовать всё через ON ERROR RESUME NEXT внутри SQL?
Стандарт SQL не содержит подобной конструкции для массового операторного импорта. В большинстве случаев отчет об ошибках формируется во внешних утилитах или в лог-таблицах, как описано выше.
Можно ли использовать транзакцию для отката только ошибочных строк?
Откатом транзакции откатываются все строки разом (atomicity). Частичное сохранение возможно только при загрузке сначала во staging-таблицу, после чего вручную делается commit успешных и log неуспешных строк.
Почему неэффективно ловить каждую ошибку INSERT через TRY/CATCH в цикле для каждой строки?
Последовательная обработка миллионов строк через одиночные INSERT + TRY/CATCH приводит к катастрофическому падению производительности по сравнению с батч или BULK INSERT + проверка после загрузки.
Компания загружала десятки тысяч строк через INSERT-скрипт, а при ошибке по уникальному ключу откатывалась вся партия. Это вызывало простои и потерю данных.
Плюсы:
Использование staging-таблицы, логирования ошибок и разделения входящих данных на категории "валидные/невалидные" позволило ускорить импорт, хранить и автоматически анализировать отказы.
Плюсы: