При интеграции систем на платформе 1С:Предприятие 7.7 с внешними базами данных DBF часто используется связка из внешней компоненты 1С++ и OLE DB провайдера от Microsoft — Visual FoxPro OLE DB Provider (VFPOLEDB). Это мощное и быстрое решение, однако разработчики регулярно сталкиваются со специфическими синтаксическими ограничениями СУБД FoxPro и особенностями обработки ошибок в среде 1С. Рассмотрим подробно, как решить проблемы с ограничением выборки строк, научимся правильно обрабатывать исключения OLE DB и разберем наиболее эффективные способы получения метаданных (структуры полей) таблиц DBF.
Часто при попытке ограничить выборку одной строкой разработчики пишут стандартный SQL-запрос, который отлично работает в MS SQL Server или MS Access:
SELECT TOP 1 * FROM MY.DBF
Однако при работе через драйвер VFPOLEDB такой запрос завершается ошибкой синтаксиса. Ошибочно полагать, что СУБД FoxPro вообще не поддерживает конструкцию TOP. На самом деле поддержка присутствует, но движок FoxPro накладывает жесткое синтаксическое ограничение: использование ключевого слова TOP обязательно требует наличия предложения ORDER BY.
Без явного указания сортировки СУБД не может определить, какие именно записи считать "первыми", и отклоняет запрос. Если вам не важен физический или логический порядок сортировки, а нужно просто получить любую первую запись, отсортируйте таблицу по физическому номеру записи с помощью системной функции RECNO(). Правильный SQL-запрос будет выглядеть следующим образом:
SELECT TOP 1 * FROM MY.DBF ORDER BY RECNO()
Существует и альтернативный классический вариант обхода этой проблемы, который не требует использования конструкции TOP и работает быстрее, так как СУБД не тратит ресурсы на сортировку данных. Мы можем ограничить выборку непосредственно через системную функцию номеров записей в условии WHERE:
SELECT * FROM MY.DBF WHERE RECNO() < 2
Оба этих метода абсолютно рабочие, но вариант с использованием WHERE RECNO() < 2 предпочтительнее с точки зрения производительности на больших объемах данных, так как он избегает фазы сортировки ORDER BY.
Разработчики часто ищут у объектов OLEDBData или OLEDBCommand встроенные методы для получения последней ошибки вроде Запрос.ошибка() или GetLastError(). В компоненте 1С++ таких явных методов нет.
При возникновении любой ошибки на стороне OLE DB провайдера (ошибка синтаксиса, отсутствие файла таблицы, блокировка файла другим процессом) компонента 1С++ генерирует стандартное runtime-исключение среды 1С. Чтобы ваша программа не аварийно завершала работу, а корректно обрабатывала и логировала эти ошибки, необходимо использовать стандартную конструкцию 1С Попытка ... Исключение.
Проанализируем пример кода, демонстрирующий правильное выполнение запроса с перехватом детального описания ошибки от провайдера VFPOLEDB:
// Создаем объекты для работы с базой через OLE DB
База = СоздатьОбъект("OLEDBData");
Соединение = "Provider=VFPOLEDB.1;Data Source=" + ПутьККаталогу + ";Mode=Read;";
Если База.Соединение(Соединение) = 0 Тогда
Предупреждение("Не удалось установить соединение с базой данных!");
Возврат;
КонецЕсли;
Запрос = База.СоздатьКоманду();
ТекстЗапроса = "SELECT TOP 1 * FROM MY.DBF ORDER BY RECNO()";
Попытка
// Выполняем инструкцию, которая должна вернуть Таблицу Значений
ТЗ = Запрос.ВыполнитьИнструкцию(ТекстЗапроса);
Исключение
// Перехватываем системную ошибку и выводим подробное описание
ТекстОшибки = ОписаниеОшибки();
Сообщить("Произошла ошибка при выполнении SQL-запроса!", "!");
Сообщить("Подробности ошибки: " + ТекстОшибки, "I");
КонецПопытки;
Внутри функции ОписаниеОшибки() будет содержаться подробный текст, переданный драйвером OLE DB. Из него вы сможете узнать точную причину сбоя (например: "Syntax error" или "File 'my.dbf' does not exist").
Получение списка колонок (имен полей) DBF-файла — стандартная задача. Чтение даже одной физической записи в ТаблицуЗначений с последующим обходом колонок в цикле, хотя и является рабочим решением, крайне неоптимально. На терабайтных базах данных или при работе по медленной сети даже чтение одной строки может вызывать задержки из-за инициализации чтения физического файла с диска.
Рассмотрим три гораздо более быстрых и элегантных способа получения структуры таблицы.
Это классический прием профессиональных разработчиков баз данных. Мы отправляем SQL-запрос, который заведомо не вернет ни одной строки данных, например, используя условие WHERE 1=0 (или WHERE .F. в синтаксисе FoxPro). СУБД мгновенно обрабатывает запрос, подготавливает структуру результирующей таблицы и возвращает ее в 1С в виде пустой ТаблицыЗначений, которая уже содержит все необходимые колонки.
// Выполняем "холостой" запрос
ТекстЗапроса = "SELECT * FROM MY.DBF WHERE 1=0";
Попытка
ПустаяТЗ = Запрос.ВыполнитьИнструкцию(ТекстЗапроса);
// Теперь мы можем легко получить имена всех полей
Для Ном = 1 По ПустаяТЗ.КоличествоКолонок() Цикл
ИмяКолонки = "";
ПустаяТЗ.ПолучитьПараметрыКолонки(Ном, , , , ИмяКолонки);
Сообщить("Найденное поле таблицы: " + ИмяКолонки);
КонецЦикла;
Исключение
Сообщить("Ошибка получения структуры: " + ОписаниеОшибки());
КонецПопытки;
Этот способ работает практически мгновенно, независимо от физического размера DBF-файла (будь он хоть несколько гигабайт), так как чтения самих данных с диска не происходит.
Если нет жесткой необходимости использовать именно SQL-соединение через 1С++, для получения структуры файла можно применить штатные средства платформы 1С 7.7. Объект XBase позволяет открыть файл в монопольном или совместном режиме чтения и быстро считать его метаданные.
ФайлDBF = СоздатьОбъект("XBase");
Попытка
ФайлDBF.ОткрытьФайл(ПутьКФайлу, , 1); // Открываем в режиме Только Чтение
КоличествоПолей = ФайлDBF.КоличествоПолей();
Для Ном = 1 По КоличествоПолей Цикл
ИмяПоля = ФайлDBF.ИмяПоля(Ном);
ТипПоля = ФайлDBF.ТипПоля(Ном);
ДлинаПоля = ФайлDBF.ДлинаПоля(Ном);
ТочностьПоля = ФайлDBF.ТочностьПоля(Ном);
Сообщить("Поле: " + ИмяПоля + " | Тип: " + ТипПоля + " | Длина: " + ДлинаПоля);
КонецЦикла;
ФайлDBF.ЗакрытьФайл();
Исключение
Сообщить("Ошибка открытия DBF: " + ОписаниеОшибки());
КонецПопытки;
Важное предупреждение: Использование встроенного объекта XBase параллельно с активным соединением OLEDBData к тому же самому файлу может вызвать конфликт совместного доступа (блокировку файла). Используйте этот метод осмотрительно и закрывайте файлы сразу после получения метаданных.
Провайдер VFPOLEDB поддерживает выполнение встроенных микро-скриптов FoxPro прямо внутри команд SQL через функцию EXECSCRIPT(). С ее помощью можно вызвать нативную команду FoxPro AFIELDS(), которая помещает детальную структуру таблицы в массив, и передать этот массив обратно в 1С. Этот способ является наиболее продвинутым, так как позволяет получить не только имена полей, но и их оригинальные типы данных FoxPro, индексы и ограничения по умолчанию напрямую из ядра базы данных.