При работе с высоконагруженными базами данных на платформе 1С 7.7 стандартные методы удаления объектов часто оказываются слишком медленными. Особенно это заметно при работе с системными таблицами, такими как _1SUPDTS (таблица регистрации изменений), где объем данных может исчисляться миллионами строк. В этой статье мы подробно разберем, как отойти от медленного удаления записей в цикле и перейти к эффективным прямым запросам к MS SQL Server, используя внешнюю компоненту 1С++.
Рассмотрим типичную ситуацию: программисту необходимо очистить таблицу от определенных записей, основываясь на значениях нескольких полей. В 1С 7.7 многие таблицы имеют составные первичные ключи (Primary Key). Например, в таблице регистрации изменений _1SUPDTS уникальность записи определяется комбинацией полей DBSIGN, TYPEID и OBJID.
Типичная, но неэффективная схема работы выглядит так:
ТаблицуЗначений.Пока ... Цикл.Такой подход создает колоссальную нагрузку на сервер из-за постоянного открытия и закрытия транзакций, а также накладных расходов на передачу команд. Проанализируем, как можно ускорить этот процесс в десятки раз.
Часто разработчики, имеющие опыт работы с PostgreSQL или Oracle, пытаются применить конструкцию сравнения кортежей:
WHERE (U.OBJID, U.DBSIGN) IN (SELECT ...)
Важно помнить, что MS SQL Server не поддерживает такой синтаксис. При попытке выполнить подобный запрос вы получите ошибку о неверном типе данных или ожидаемом логическом выражении. Поэтому единственным верным и производительным способом для массовых операций с составными ключами в 1С 7.7 является использование временных таблиц и соединения INNER JOIN.
Для начала нам необходимо передать список идентификаторов, подлежащих удалению, из оперативной памяти 1С в SQL Server. Рассмотрим пример использования метода ВыполнитьSQL_ИзТЗ компоненты 1С++. Этот метод позволяет мгновенно наполнить физическую таблицу на сервере данными из 1С.
Создадим и подготовим временную таблицу:
// Создаем объект для работы с прямыми запросами
RS = СоздатьОбъект("ODBCRecordset");
// Определяем структуру временной таблицы в tempdb
// Используем тип Char(9) для идентификаторов 1С и Char(3) для кода базы
RS.ВыполнитьСкалярный("
|IF OBJECT_ID('tempdb..#тбУдалять', 'U') IS NOT NULL
| DROP TABLE #тбУдалять;
|CREATE TABLE #тбУдалять (OBJID Char(9), DBSIGN Char(3));");
// Подготавливаем инструкцию вставки
RS.Подготовить("
|insert into #тбУдалять(OBJID, DBSIGN)
|values(?, ?)");
// тбУдалять — это обычная ТаблицаЗначений в 1С с колонками OBJID и DBSIGN
RS.ВыполнитьSQL_ИзТЗ(тбУдалять);
После того как данные загружены во временную таблицу #тбУдалять, мы можем выполнить операцию удаления одним запросом. Это позволяет SQL-серверу построить оптимальный план выполнения и обработать данные пакетом.
Разберем структуру эффективного запроса DELETE:
ТекстЗапроса = "
|DELETE
| U
|FROM
| _1SUPDTS as U
|INNER JOIN
| #тбУдалять as T ON U.OBJID = T.OBJID AND U.DBSIGN = T.DBSIGN";
RS.ВыполнитьИнструкцию(ТекстЗапроса);
В данном примере мы используем алиас U для целевой таблицы. Обратите внимание на условие ON: мы связываем таблицы по всем полям, входящим в составной ключ. Это критически важно для производительности, так как позволяет серверу использовать существующие индексы основной таблицы.
Выясним, какие еще нюансы стоит учитывать при работе с прямыми запросами в 1С 7.7:
Char(9), то и во временной таблице оно должно быть Char(9). Использование Varchar может привести к неявному преобразованию типов (Implicit Conversion), из-за чего SQL Server проигнорирует индексы и выполнит полное сканирование таблицы (Table Scan).DELETE накладывает блокировки на строки или всю таблицу. Старайтесь выполнять такие операции в периоды минимальной активности пользователей.Проанализируем итоговый код, который объединяет выборку условий и последующее удаление. Предположим, нам нужно удалить регистрации изменений для документов, которые не соответствуют определенному складу.
// 1. Формируем ТЗ с данными для анализа в 1С
// ... (заполнение тбОбмен данными из запроса) ...
тбУдалять = СоздатьОбъект("ТаблицаЗначений");
тбУдалять.НоваяКолонка("OBJID");
тбУдалять.НоваяКолонка("DBSIGN");
тбОбмен.ВыбратьСтроки();
Пока тбОбмен.ПолучитьСтроку() = 1 Цикл
Если НужноУдалить(тбОбмен.Док, тбОбмен.Склад) = 1 Тогда
тбУдалять.НоваяСтрока();
тбУдалять.OBJID = тбОбмен.OBJID;
тбУдалять.DBSIGN = тбОбмен.DBSIGN;
КонецЕсли;
КонецЦикла;
// 2. Передаем ТЗ в SQL и удаляем
RS = СоздатьОбъект("ODBCRecordset");
RS.Выполнить("IF OBJECT_ID('tempdb..#тбУдалять', 'U') IS NOT NULL DROP TABLE #тбУдалять;");
RS.Выполнить("CREATE TABLE #тбУдалять (OBJID Char(9), DBSIGN Char(3));");
RS.Подготовить("INSERT INTO #тбУдалять (OBJID, DBSIGN) VALUES (?, ?);");
RS.ВыполнитьSQL_ИзТЗ(тбУдалять);
ТекстЗапроса = "
|DELETE _1SUPDTS
|FROM _1SUPDTS AS U
|INNER JOIN #тбУдалять AS T ON U.OBJID = T.OBJID AND U.DBSIGN = T.DBSIGN";
RS.ВыполнитьИнструкцию(ТекстЗапроса);
Сообщить("Очистка завершена успешно!");
Использование этого метода позволяет сократить время выполнения операций с нескольких часов до нескольких минут, что является критически важным фактором при обслуживании крупных баз данных на платформе 1С 7.7.