В процессе сопровождения баз данных 1С часто возникает задача проверки корректности заполнения справочников. Одной из наиболее типичных проблем является появление дублей по определенному реквизиту. Например, когда в справочнике ТО (Техническое обслуживание) для разных элементов ошибочно указан один и тот же Склад. В данной статье мы подробно разберем, как выявить такие записи с помощью языка запросов, проанализируем различные подходы к оптимизации и рассмотрим инструменты для предотвращения подобных ситуаций.
Начнем с самого прямолинейного способа. Представим ситуацию: нам нужно вывести список всех ссылок справочника ТО, у которых значение реквизита Склад встречается в базе более одного раза. Проанализируем базовую структуру такого запроса.
Основная идея заключается в том, чтобы сначала определить список «проблемных» складов, а затем выбрать все элементы, которые на них ссылаются. Разберем по шагам логику построения условия ГДЕ ... В (ВЫБРАТЬ ...).
ВЫБРАТЬ
ТО.Ссылка КАК Ссылка,
ТО.Склад КАК Склад
ИЗ
Справочник.ТО КАК ТО
ГДЕ
ТО.Склад В
(ВЫБРАТЬ
ТО_Дубли.Склад
ИЗ
Справочник.ТО КАК ТО_Дубли
СГРУППИРОВАТЬ ПО
ТО_Дубли.Склад
ИМЕЮЩИЕ
КОЛИЧЕСТВО(ТО_Дубли.Ссылка) > 1)
В данном примере внутренний запрос выполняет группировку по полю Склад. Оператор ИМЕЮЩИЕ (аналог HAVING в SQL) позволяет нам отфильтровать результаты агрегатной функции КОЛИЧЕСТВО. Если количество ссылок для конкретного склада больше единицы, этот склад попадает в список дублей.
Рассмотрим ситуацию, когда база данных содержит сотни тысяч записей. Выполнение вложенного запроса внутри условия ГДЕ может привести к деградации производительности, так как СУБД не всегда эффективно строит план выполнения для таких конструкций. Профессиональные разработчики рекомендуют использовать временные таблицы (МенеджерВременныхТаблиц).
Выясним причину, почему это эффективнее: при использовании временных таблиц 1С сначала вычисляет результат промежуточного запроса, сохраняет его в памяти или в специальной таблице SQL-сервера, а затем выполняет конечное соединение. Это упрощает работу оптимизатора СУБД.
// Шаг 1: Помещаем дубликаты во временную таблицу
ВЫБРАТЬ
ТО.Склад КАК Склад
ПОМЕСТИТЬ ВТ_ДублиСкладов
ИЗ
Справочник.ТО КАК ТО
СГРУППИРОВАТЬ ПО
ТО.Склад
ИМЕЮЩИЕ
КОЛИЧЕСТВО(ТО.Ссылка) > 1
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Выбираем все данные, сопоставляя с временной таблицей
ВЫБРАТЬ
ТО.Ссылка КАК Ссылка,
ТО.Склад КАК Склад
ИЗ
Справочник.ТО КАК ТО
ГДЕ
ТО.Склад В (ВЫБРАТЬ ВТ.Склад ИЗ ВТ_ДублиСкладов КАК ВТ)
УПОРЯДОЧИТЬ ПО
Склад
ИТОГИ ПО
Склад
Обратите внимание на использование секции ИТОГИ. Это позволяет визуально сгруппировать дубли в отчете или при выводе в дерево значений. Мы сразу видим склад-заголовок и под ним список всех элементов ТО, которые к нему привязаны.
Существует еще один способ решения задачи — через ВНУТРЕННЕЕ СОЕДИНЕНИЕ таблицы самой с собой. Посмотрим на пример реализации:
ВЫБРАТЬ
ТО1.Ссылка КАК Ссылка,
ТО1.Склад КАК Склад
ИЗ
Справочник.ТО КАК ТО1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
Справочник.ТО КАК ТО2
ПО (ТО1.Склад = ТО2.Склад) И (ТО1.Ссылка <> ТО2.Ссылка)
СГРУППИРОВАТЬ ПО
ТО1.Ссылка,
ТО1.Склад
Здесь мы соединяем таблицу с её копией по совпадению склада, но при условии, что ссылки на элементы разные (ТО1.Ссылка <> ТО2.Ссылка). Если для элемента ТО1 найдется хотя бы один «собрат» с таким же складом, запись попадет в результат. Однако следует помнить, что на очень больших таблицах операция соединения может быть «дорогой» с точки зрения ресурсов сервера.
Проанализируем техническую сторону вопроса производительности. Любой запрос, который ищет дубли, будет работать быстро только в том случае, если по проверяемому полю построен индекс. Если у реквизита Склад в метаданных не стоит свойство Индексировать, системе придется выполнять полное сканирование всей таблицы (Table Scan). Для справочников с объемом записей более 10 000 это критично. Всегда проверяйте наличие индекса у полей, по которым планируете часто искать совпадения.
Если ваша конфигурация разработана на базе Библиотеки стандартных подсистем (БСП), нет необходимости писать код для разовой проверки. Рассмотрим встроенную обработку Поиск и удаление дублей. Разберем её преимущества:
Чтобы не заниматься поиском ошибок постфактум, разберем, как предотвратить их появление на этапе ввода. Самый надежный способ — программная проверка в модуле объекта справочника. Рассмотрим пример кода, который следует разместить в обработчике события ПередЗаписью:
Процедура ПередЗаписью(Отказ)
// Проверка на дубли склада при записи
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| ТО.Ссылка КАК Ссылка
|ИЗ
| Справочник.ТО КАК ТО
|ГДЕ
| ТО.Склад = &Склад
| И ТО.Ссылка <> &Ссылка";
Запрос.УстановитьПараметр("Склад", Склад);
Запрос.УстановитьПараметр("Ссылка", Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Внимание! Элемент с таким складом уже существует.";
Сообщение.Сообщить();
// Отказ = Истина; // Разкомментируйте, если хотите запретить запись
КонецЕсли;
КонецПроцедуры
Таким образом, мы обеспечиваем чистоту данных в режиме реального времени. Если ваша задача — строгий контроль, используйте флаг Отказ = Истина, чтобы заблокировать запись некорректных данных.
Мы рассмотрели несколько подходов к поиску дублей в 1С: от простых запросов с ИМЕЮЩИЕ до использования временных таблиц и механизмов БСП. Выбор конкретного метода зависит от объема данных и частоты возникновения задачи. Для разового анализа идеально подходит консоль запросов, а для поддержания порядка в «боевой» базе лучше использовать комбинацию из типовых обработок и превентивных проверок в коде конфигурации.