Как выполнить несколько SQL-запросов к MySQL в одной строке через ADODB?

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

При интеграции 1С с внешними базами данных, такими как MySQL, программисты часто сталкиваются с необходимостью оптимизировать количество обращений к серверу. Одной из типичных задач является выполнение нескольких связанных операций (например, INSERT и UPDATE) за один вызов. По умолчанию попытка выполнить строку запросов, разделенных точкой с запятой, через ADODB.Command часто приводит к синтаксической ошибке. В этой статье мы подробно разберем, почему это происходит и как правильно реализовать пакетное выполнение запросов.

Выясняем причину ошибки: почему MySQL блокирует пакетные запросы

Разберем ситуацию, с которой столкнулся автор темы. В его коде происходит конкатенация (склеивание) двух разных инструкций: добавление записи в таблицу акций и обновление остатков в основной таблице. На первый взгляд, использование точки с запятой кажется логичным, так как в консоли MySQL такой синтаксис работает. Однако при работе через ADODB вступают в силу настройки драйвера и соображения безопасности.

Основная причина ошибки заключается в том, что драйверы (ODBC или OLE DB) по умолчанию отключают поддержку мультизапросов (multi-statements) для предотвращения SQL-инъекций. Если злоумышленник сможет внедрить точку с запятой в поле ввода, он может дописать любой деструктивный код (например, DROP TABLE). Поэтому драйвер ожидает только одну команду на одно выполнение.

Решение 1: Настройка строки подключения (Allow Multi Queries)

Для того чтобы MySQL позволил выполнить пакет из нескольких команд, нам необходимо явно указать это в строке подключения (ConnectionString). Рассмотрим параметры, которые нужно добавить в зависимости от используемого драйвера:

  1. MySQL ODBC Driver: Если вы используете стандартный ODBC, необходимо добавить параметр OPTION=67108864. Эта битовая маска сообщает драйверу, что выполнение нескольких инструкций разрешено.
  2. Современные драйверы (MySQL Connector/NET и аналоги): Часто используется параметр 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;";

Решение 2: Отказ от "страшного" кода и переход на параметры

В обсуждении на форуме справедливо было замечено, что ручная склейка строки запроса через + и Формат() — это плохая практика. Давайте разберем, чем опасен такой подход и как его улучшить.

Проблемы склейки строк:

Рассмотрим правильный способ реализации через объект 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();

Решение 3: Использование транзакций для целостности данных

Посмотрим на ситуацию со стороны надежности. Если первый запрос (INSERT) выполнится успешно, а на втором (UPDATE) произойдет сбой (например, из-за блокировки таблицы другим процессом), ваши данные станут неконсистентными: скидка в одной таблице обновится, а цена в другой останется старой.

Для решения этой проблемы мы должны использовать транзакции. Вместо того чтобы объединять все в одну текстовую строку, мы можем выполнить команды последовательно, но обернуть их в общую логическую единицу. Рассмотрим пример:


Попытка
    Connection.BeginTrans(); // Начинаем транзакцию
    
    // Выполняем первый запрос
    Command.CommandText = "INSERT INTO ...";
    Command.Execute();
    
    // Выполняем второй запрос
    Command.CommandText = "UPDATE ...";
    Command.Execute();
    
    Connection.CommitTrans(); // Если все ок, фиксируем изменения
    Сообщить("Данные успешно обновлены!");
Исключение
    Connection.RollbackTrans(); // Если была ошибка, откатываем всё назад
    Сообщить("Ошибка при обновлении базы: " + ОписаниеОшибки());
КонецПопытки;

Решение 4: Обработка результатов через NextRecordset

Если ваши запросы не просто записывают данные, а возвращают результат (например, два разных SELECT), вам понадобится метод NextRecordset. Как упоминалось в сообщениях форума, ADO умеет работать с составными результатами.

Проанализируем алгоритм:

  1. Выполняем пакетный запрос методом Execute.
  2. Получаем первый Recordset.
  3. После обработки первой порции данных вызываем Recordset = Recordset.NextRecordset().
  4. Если объект не пуст, обрабатываем второй набор данных.

Важное замечание: Не все драйверы MySQL стабильно поддерживают NextRecordset. Если вы столкнулись с тем, что возвращается только первый результат, проверьте версию вашего ODBC-драйвера (рекомендуется 8.0 и выше).

Подведение итогов

Мы выяснили, что выполнить несколько запросов в одной строке через ADODB вполне реально, но это требует правильной настройки ConnectionString (параметр OPTION или AllowMultiQueries). Однако, с точки зрения архитектуры 1С, более правильным и надежным подходом является использование **транзакций** и **параметризованных запросов**. Это защищает базу данных от некорректных форматов и гарантирует, что либо все изменения будут применены вместе, либо ни одно из них не попадет в базу при возникновении ошибки.

← На главную