При интеграции 1С с внешними базами данных, такими как MySQL, программисты часто сталкиваются с необходимостью оптимизировать количество обращений к серверу. Одной из типичных задач является выполнение нескольких связанных операций (например, INSERT и UPDATE) за один вызов. По умолчанию попытка выполнить строку запросов, разделенных точкой с запятой, через ADODB.Command часто приводит к синтаксической ошибке. В этой статье мы подробно разберем, почему это происходит и как правильно реализовать пакетное выполнение запросов.
Разберем ситуацию, с которой столкнулся автор темы. В его коде происходит конкатенация (склеивание) двух разных инструкций: добавление записи в таблицу акций и обновление остатков в основной таблице. На первый взгляд, использование точки с запятой кажется логичным, так как в консоли MySQL такой синтаксис работает. Однако при работе через ADODB вступают в силу настройки драйвера и соображения безопасности.
Основная причина ошибки заключается в том, что драйверы (ODBC или OLE DB) по умолчанию отключают поддержку мультизапросов (multi-statements) для предотвращения SQL-инъекций. Если злоумышленник сможет внедрить точку с запятой в поле ввода, он может дописать любой деструктивный код (например, DROP TABLE). Поэтому драйвер ожидает только одну команду на одно выполнение.
Для того чтобы MySQL позволил выполнить пакет из нескольких команд, нам необходимо явно указать это в строке подключения (ConnectionString). Рассмотрим параметры, которые нужно добавить в зависимости от используемого драйвера:
OPTION=67108864. Эта битовая маска сообщает драйверу, что выполнение нескольких инструкций разрешено.AllowMultiQueries=True или Allow Multi Queries=True.Проанализируем, как может выглядеть модифицированная строка подключения:
// Пример для ODBC
СтрокаПодключения = "DRIVER={MySQL ODBC 8.0 Unicode Driver};Server=localhost;Database=my_db;User=root;Password=pass;OPTION=67108864;";
В обсуждении на форуме справедливо было замечено, что ручная склейка строки запроса через + и Формат() — это плохая практика. Давайте разберем, чем опасен такой подход и как его улучшить.
Проблемы склейки строк:
Строка.КодТовара встретится кавычка, весь запрос "развалится" с синтаксической ошибкой.Рассмотрим правильный способ реализации через объект Command и параметры. Это не только безопаснее, но и избавляет от необходимости заботиться о кавычках и форматах чисел.
Command = Новый COMОбъект("ADODB.Command");
Command.ActiveConnection = Connection; // Укажите ваше соединение
Command.NamedParameters = Истина;
// Текст запроса с плейсхолдерами (знаками вопроса или именованными параметрами)
// В MySQL через ODBC чаще используются знаки вопроса
Command.CommandText = "INSERT INTO table_promo (product_id, price) VALUES (?, ?);
UPDATE table_stock SET quantity = ?, price = ? WHERE model = ?;";
// Добавляем параметры по порядку их следования в запросе
Command.Parameters.Append(Command.CreateParameter("prod_id", 3, 1, , Строка.Код)); // 3 - adInteger
Command.Parameters.Append(Command.CreateParameter("price_disc", 14, 1, , Строка.ЦенаСкидка)); // 14 - adDecimal
Command.Parameters.Append(Command.CreateParameter("qty", 14, 1, , Строка.Остаток));
Command.Parameters.Append(Command.CreateParameter("price", 14, 1, , Строка.Цена));
Command.Parameters.Append(Command.CreateParameter("model", 200, 1, 50, Строка.КодТовара)); // 200 - adVarChar
Command.Execute();
Посмотрим на ситуацию со стороны надежности. Если первый запрос (INSERT) выполнится успешно, а на втором (UPDATE) произойдет сбой (например, из-за блокировки таблицы другим процессом), ваши данные станут неконсистентными: скидка в одной таблице обновится, а цена в другой останется старой.
Для решения этой проблемы мы должны использовать транзакции. Вместо того чтобы объединять все в одну текстовую строку, мы можем выполнить команды последовательно, но обернуть их в общую логическую единицу. Рассмотрим пример:
Попытка
Connection.BeginTrans(); // Начинаем транзакцию
// Выполняем первый запрос
Command.CommandText = "INSERT INTO ...";
Command.Execute();
// Выполняем второй запрос
Command.CommandText = "UPDATE ...";
Command.Execute();
Connection.CommitTrans(); // Если все ок, фиксируем изменения
Сообщить("Данные успешно обновлены!");
Исключение
Connection.RollbackTrans(); // Если была ошибка, откатываем всё назад
Сообщить("Ошибка при обновлении базы: " + ОписаниеОшибки());
КонецПопытки;
Если ваши запросы не просто записывают данные, а возвращают результат (например, два разных SELECT), вам понадобится метод NextRecordset. Как упоминалось в сообщениях форума, ADO умеет работать с составными результатами.
Проанализируем алгоритм:
Execute.Recordset.Recordset = Recordset.NextRecordset().Важное замечание: Не все драйверы MySQL стабильно поддерживают NextRecordset. Если вы столкнулись с тем, что возвращается только первый результат, проверьте версию вашего ODBC-драйвера (рекомендуется 8.0 и выше).
Мы выяснили, что выполнить несколько запросов в одной строке через ADODB вполне реально, но это требует правильной настройки ConnectionString (параметр OPTION или AllowMultiQueries). Однако, с точки зрения архитектуры 1С, более правильным и надежным подходом является использование **транзакций** и **параметризованных запросов**. Это защищает базу данных от некорректных форматов и гарантирует, что либо все изменения будут применены вместе, либо ни одно из них не попадет в базу при возникновении ошибки.