Почему пакетный SQL-запрос к MySQL работает в phpMyAdmin, но падает с ошибкой из 1С?

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

При интеграции 1С со сторонними базами данных MySQL через COM-объект ADO или ODBC-провайдер разработчики часто сталкиваются с ситуацией, когда строка из нескольких инструкций INSERT, разделенных точкой с запятой, успешно отрабатывает в веб-интерфейсе phpMyAdmin, но при вызове метода Execute() из 1С завершается ошибкой синтаксиса:

Microsoft OLE DB Provider for ODBC Drivers: [MySQL][ODBC Driver] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO...

Проанализируем причины такого поведения и разберем по шагам надежные способы передачи данных из 1С в MySQL.

Выясним причину ошибки

Главная причина заключается в том, что драйвер MySQL Connector/ODBC по умолчанию запрещает выполнение составных пакетов команд (Multi-Statement / Batch execution) в рамках одного вызова из соображений безопасности (защита от внедрения вредоносного SQL-кода). Панели администрирования баз данных (такие как phpMyAdmin или DBeaver) либо предварительно разбивают текст скрипта на отдельные команды перед отправкой на сервер, либо изначально подключаются с активным флагом пакетных запросов.

Когда мы передаем в метод mySQL.Execute(Стр) строку, содержащую несколько операторов INSERT через ;, драйвер считает точку с запятой синтаксической ошибкой и прерывает выполнение на второй команде.

Способ 1. Использование единого оператора массовой вставки (Bulk INSERT)

Вместо отправки множества отдельных команд INSERT INTO ... VALUES (...); INSERT INTO ... стандартный синтаксис MySQL позволяет вставить множество строк одним SQL-выражением через запятую. Этот способ является оптимальным: он не требует изменения настроек драйвера и работает значительно быстрее одиночных вставок.

Посмотрим на пример формирования такого запроса в цикле выборки 1С:


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

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

МассивСтрокЗначений = Новый Массив;

Пока Выборка.Следующий() Цикл
    Гуид = Строка(Выборка.Ссылка.УникальныйИдентификатор());
    НомерДок = СтрЗаменить(Выборка.Номер, """", "'");
    
    // Форматируем дату в стандартный для MySQL формат YYYY-MM-DD HH:MM:SS
    ДатаСтр = Формат(Выборка.Дата, "ДФ=""yyyy-MM-dd HH:mm:ss""");
    
    Неисправность = СтрЗаменить(Выборка.Неисправность, """", "'");
    ФИО = СтрЗаменить(Выборка.ФИО, """", "'");
    
    СтрокаЗначений = "(""" + Гуид + """, """ + НомерДок + """, """ + ДатаСтр + """, """ + Неисправность + """, """ + ФИО + """)";
    МассивСтрокЗначений.Добавить(СтрокаЗначений);
КонецЦикла;

Если МассивСтрокЗначений.Количество() > 0 Тогда
    ТекстSQL = "INSERT INTO `repairs_statuses` (`id`, `number`, `date`, `fault`, `fio`) VALUES " 
        + СтрСоединить(МассивСтрокЗначений, ", ");
    
    mySQLConnection.Execute(ТекстSQL);
КонецЕсли;

Способ 2. Построчное выполнение запросов в цикле

Если записей немного или логика обработки требует фиксировать результат выполнения каждой отдельной операции, выполним отправку запроса для каждого документа индивидуально на каждой итерации цикла:


Пока Выборка.Следующий() Цикл
    Гуид = Строка(Выборка.Ссылка.УникальныйИдентификатор());
    НомерДок = СтрЗаменить(Выборка.Номер, """", "'");
    ДатаСтр = Формат(Выборка.Дата, "ДФ=""yyyy-MM-dd HH:mm:ss""");
    Неисправность = СтрЗаменить(Выборка.Неисправность, """", "'");
    ФИО = СтрЗаменить(Выборка.ФИО, """", "'");
    
    ТекстSQL = "INSERT INTO `repairs_statuses` (`id`, `number`, `date`, `fault`, `fio`) "
        + "VALUES (""" + Гуид + """, """ + НомерДок + """, """ + ДатаСтр + """, """ + Неисправность + """, """ + ФИО + """);";
    
    mySQLConnection.Execute(ТекстSQL);
    
    ОбработкаПрерыванияПользователя();
КонецЦикла;

Способ 3. Включение поддержки множественных запросов в драйвере ODBC

Если необходимо передавать именно пачку разнородных запросов в одной строке, разрешим драйверу исполнение множественных выражений (Multi-Statements):

  1. В строке подключения: добавим параметр MULTI_STATEMENTS=1; или флаг опции OPTION=67108864; (соответствует битовой маске 0x04000000).
  2. В системном DSN Windows: в окне настройки источника данных ODBC на вкладке дополнительных параметров (Flags / Details) установим флаг Allow multiple statements.

Важные нюансы при выгрузке данных из 1С в MySQL

  1. Формат даты и времени: 1С при неявном приведении даты к строке формирует представление вида ДД.ММ.ГГГГ ЧЧ:ММ:СС. MySQL ожидает дату по стандарту ГГГГ-ММ-ДД ЧЧ:ММ:СС. Обязательно форматируем даты через Формат(Значение, "ДФ=""yyyy-MM-dd HH:mm:ss""").
  2. Разрядность драйвера: разрядность установленного ODBC-драйвера MySQL (32-bit или 64-bit) должна строго соответствовать разрядности исполняемого клиента 1С или сервера 1С:Предприятия. Для 32-разрядного клиента настройка DSN производится через C:\Windows\SysWOW64\odbcad32.exe.
  3. Кодировка соединения: чтобы избежать появления знаков вопроса вместо кириллицы, укажем в параметрах подключения CHARSET=utf8mb4 или выполним команду SET NAMES utf8mb4 сразу после открытия соединения.
← На главную