Почему при создании DBF-файла в него записывается только одна строка вместо всех данных?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке механизмов выгрузки данных из 1С в формат DBF программисты часто сталкиваются с ситуацией, когда основной запрос возвращает множество строк, но в итоговом файле оказывается лишь одна запись. Рассмотрим, почему это происходит, разберем типичную ошибку в логике циклов и изучим правильные методы работы с объектом XBase.

Анализ основной ошибки: Переопределение итератора

Разберем ситуацию, представленную в вопросе. Программист использует цикл Пока ВыборкаДетальныеЗаписи.Следующий() Цикл для обхода результатов основного запроса. Внутри этого цикла выполняется еще один запрос (для получения ФИО физического лица), и результат этого вложенного запроса присваивается той же самой переменной — ВыборкаДетальныеЗаписи.

Проанализируем, что происходит в памяти системы:

  1. Первая итерация основного цикла начинается успешно. Переменная ВыборкаДетальныеЗаписи указывает на первую строку основного результата.
  2. Внутри цикла создается новый объект Запрос и выполняется.
  3. Команда ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать() уничтожает связь переменной с основным набором данных и заменяет её новой выборкой (из вложенного запроса).
  4. Вложенный цикл по ФИО отрабатывает корректно.
  5. Когда управление возвращается к началу основного цикла, условие ВыборкаДетальныеЗаписи.Следующий() проверяется уже для выборки ФИО, которая к этому моменту закончена.
  6. Цикл завершается, так как "новая" выборка больше не имеет записей.

Решение "в лоб": Использование уникального имени переменной для каждой выборки. Например, для внутреннего запроса следует использовать ВыборкаФИО или ВыборкаДетальныеЗаписи2. Однако это лишь исправление симптома, а не самой архитектурной проблемы кода.

Оптимальное решение: Избавление от запросов в цикле

Выполнение запроса внутри цикла — это серьезная ошибка производительности (антипаттерн "N+1"). Если в вашей основной выборке 1000 записей, система обратится к базе данных 1001 раз. Вместо этого мы должны использовать возможности языка SQL (в терминах 1С — языка запросов) и объединить данные одним махом.

Рассмотрим, как должен выглядеть правильный запрос с использованием ЛЕВОЕ СОЕДИНЕНИЕ:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ОсновныеДанные.Код КАК Код,
    |	ОсновныеДанные.Ссылка.КодПоДРФО КАК КодПоДРФО,
    |	ОсновныеДанные.Ссылка.Наименование КАК Наименование,
    |	ОсновныеДанные.ДокументВид КАК ДокументВид,
    |	ОсновныеДанные.ДокументСерия КАК ДокументСерия,
    |	ОсновныеДанные.ДокументНомер КАК ДокументНомер,
    |	ФИОФизЛицСрезПоследних.Фамилия КАК Фамилия,
    |	ФИОФизЛицСрезПоследних.Имя КАК Имя,
    |	ФИОФизЛицСрезПоследних.Отчество КАК Отчество
    |ИЗ
    |	Справочник.Сотрудники КАК ОсновныеДанные
    |		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ФИОФизЛиц.СрезПоследних(&Период, ) КАК ФИОФизЛицСрезПоследних
    |		ПО ОсновныеДанные.Физлицо = ФИОФизЛицСрезПоследних.ФизЛицо
    |ГДЕ
    |	ОсновныеДанные.Ссылка В(&МассивСотрудников)";

Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("МассивСотрудников", СписокСотрудников);

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    ДБФ.Добавить();
    ДБФ.CODE      = Выборка.Код;
    ДБФ.LASTNAME  = Выборка.Фамилия;
    ДБФ.FIRSTNAME = Выборка.Имя;
    // ... заполнение остальных полей ...
    ДБФ.Записать();
КонецЦикла;

Такой подход позволяет получить все необходимые данные за один вызов сервера БД, что ускоряет выполнение задачи в десятки раз и полностью устраняет проблему с "потерянными" строками.

Работа с объектом XBase: Тонкости и секреты

Создание DBF-файла через встроенный объект 1С XBase требует соблюдения определенной последовательности действий и понимания того, как устроены эти файлы.

1. Инициализация структуры. Перед записью данных необходимо программно создать поля. Если вы открываете существующий файл, используйте ОткрытьФайл(), если создаете новый — СоздатьФайл(). Важно помнить, что длина поля в DBF жестко ограничена.

2. Кодировка. Это "камень преткновения" при передаче файлов в госструктуры. Большинство старых систем (например, банковские клиенты или налоговые программы) используют кодировку DOS (OEM). В 1С это задается свойством:

ДБФ.Кодировка = КодировкаXBase.OEM; (для кодировки DOS)

ДБФ.Кодировка = КодировкаXBase.ANSI; (для кодировки Windows-1251)

3. Метод Записать() и Добавить(). Разберем разницу. Метод Добавить() фактически выделяет место под новую строку в буфере. После этого вы заполняете поля. Метод Записать() переносит данные из буфера непосредственно в файл на диске. Если вы забудете вызвать Записать() внутри цикла, ваши данные не сохранятся.

Обработка типов данных при выгрузке

DBF крайне чувствителен к типам. Проанализируем типичные проблемы:

Альтернативный метод: Использование ADO

В некоторых случаях стандартный объект XBase может работать недостаточно быстро или иметь проблемы с чтением специфических диалектов DBF. Профессиональные разработчики иногда используют технологию ADO (ActiveX Data Objects). Это позволяет работать с DBF-файлом как с таблицей базы данных, используя привычные SQL-команды INSERT INTO.

Пример логики через ADO (схематично):


Connection = Новый COMОбъект("ADODB.Connection");
ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + ПутьККаталогу + ";Extended Properties=DBASE IV;";
Connection.Open(ConnectionString);
Command = Новый COMОбъект("ADODB.Command");
Command.ActiveConnection = Connection;
Command.CommandText = "INSERT INTO file.dbf (CODE, NAME) VALUES ('001', 'Тест')";
Command.Execute();
Connection.Close();

Этот метод считается более "тяжелым", но он незаменим, когда требуется высокая точность управления типами данных или работа с файлами через сетевые драйверы.

Рекомендации по завершению работы

Всегда закрывайте файл методом ЗакрытьФайл(). Если ваша обработка аварийно завершится до вызова этого метода, файл может остаться заблокированным операционной системой, и вы не сможете его открыть, удалить или перезаписать до перезагрузки сеанса 1С или даже всего компьютера. Также хорошим тоном считается проверка на существование файла перед созданием (использование функции НайтиФайлы()) и его удаление, если вы хотите создать чистую выгрузку.

Подведем итог: чтобы ваша выгрузка работала корректно, никогда не переиспользуйте переменные итераторов, старайтесь минимизировать количество обращений к БД за счет соединений в запросах и всегда явно указывайте кодировку создаваемого DBF-файла.

← На главную