При разработке печатных форм в системе 1С программисты часто сталкиваются с ситуацией, когда одна и та же информация (например, доверенность, подпись или строка номенклатуры) выводится несколько раз, хотя должна быть представлена в единственном экземпляре. Чаще всего это происходит из-за специфики работы механизмов запросов при обращении к табличным частям документа или при использовании сложных соединений в запросах. Рассмотрим подробнее, почему возникают дубли и как их правильно устранить, используя профессиональные инструменты платформы 1С.
Проанализируем ситуацию, представленную в коде. Когда мы соединяем основную таблицу (например, документ СчетНаОплатуПокупателю) с его табличной частью или связанным справочником без четкого условия уникальности, СУБД формирует так называемое "декартово произведение". Если в документе есть табличная часть "Заявки на курс" и в двух строках указана одна и та же информация, обычное левое соединение (LEFT JOIN) вернет две строки вместо одной.
Выясним причину на конкретном примере запроса. Если в условии ГДЕ или в секции ПО соединения не указаны уникальные идентификаторы, а используются общие поля (например, организация или подразделение), система подтянет все записи, соответствующие этим полям. В результате в выборку попадает количество строк, кратное количеству записей в соединяемых таблицах. Использование конструкции ВЫБРАТЬ РАЗЛИЧНЫЕ помогает не всегда, так как даже минимальное различие в скрытых полях (например, разные ссылки на строки ТЧ) сделает записи "различными" для системы.
Самым правильным и эффективным способом избавления от дублей является перенос данных во временные таблицы с последующей группировкой. Разберем по шагам, как это реализовать:
СГРУППИРОВАТЬ ПО, чтобы оставить только уникальные значения по ключевым полям.Посмотрим на пример кода, который заменяет проблемный запрос:
// Создаем запрос с использованием временных таблиц
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Счет.Ссылка КАК Ссылка,
| Счет.Организация КАК Организация
|ПОМЕСТИТЬ ВТ_УникальныеДокументы
|ИЗ
| Документ.СчетНаОплатуПокупателю КАК Счет
|ГДЕ
| Счет.Ссылка = &Ссылка;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ.Организация КАК Организация,
| Основания.Ссылка КАК ОснованиеПодписи,
| Основания.Наименование КАК Наименование
|ИЗ
| ВТ_УникальныеДокументы КАК ВТ
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ОснованияПраваПодписи КАК Основания
| ПО ВТ.Организация = Основания.Организация
|ГДЕ
| Основания.Сотрудник = &Сотрудник
|СГРУППИРОВАТЬ ПО
| ВТ.Организация,
| Основания.Ссылка,
| Основания.Наименование";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
Запрос.УстановитьПараметр("Сотрудник", ВыбранныйСотрудник);
Результат = Запрос.Выполнить();
В исходном сообщении была замечена критическая ошибка: запрос выполняется внутри цикла Пока Выборка.Следующий() Цикл. Это не только замедляет работу системы, но и является основной причиной появления дублей в печатной форме, так как на каждую итерацию внешнего цикла порождается новый набор данных.
Рассмотрим, как это исправить. Мы должны сначала собрать все необходимые данные (параметры) в массив, передать этот массив в один-единственный запрос, получить результат и уже его выводить в печатную форму. Это гарантирует контроль над уникальностью записей на уровне одного результирующего набора данных.
Если данные для печатной формы предварительно загружаются в промежуточную таблицу значений (ТЗ), мы можем программно удалить дубликаты. Проанализируем использование метода Свернуть(). Этот метод позволяет мгновенно объединить одинаковые строки.
Пример использования:
// Предположим, ТЗ_Данные содержит колонки "Сотрудник", "Должность", "Доверенность"
// Указываем колонки, по которым нужно сгруппировать данные
КолонкиГруппировки = "Сотрудник, Должность, Доверенность";
// Колонки суммирования оставляем пустыми, если нам нужно просто удалить дубли
КолонкиСуммирования = "";
ТЗ_Данные.Свернуть(КолонкиГруппировки, КолонкиСуммирования);
// Теперь ТЗ_Данные содержит только уникальные комбинации строк
Для Каждого СтрокаДанных Из ТЗ_Данные Цикл
// Вывод в печатную форму
КонецЦикла;
Иногда структура данных слишком сложна для быстрой переработки запроса. В таких случаях можно применить проверку "на лету" перед выводом области в табличный документ. Для этого удобно использовать объект Соответствие или Массив, в котором мы будем хранить уже выведенные ключи (например, ссылки на доверенности).
Разберем этот метод по шагам:
ВыведенныеСсылки = Новый Массив; перед началом цикла вывода.Найти().
ВыведенныеСсылки = Новый Массив;
Пока Выборка.Следующий() Цикл
Если ВыведенныеСсылки.Найти(Выборка.Доверенность) = Неопределено Тогда
// Заполняем параметры области
ОбластьМакета.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОбластьМакета);
// Запоминаем, что мы уже вывели эту доверенность
ВыведенныеСсылки.Добавить(Выборка.Доверенность);
КонецЕсли;
КонецЦикла;
Часто ВЫБРАТЬ РАЗЛИЧНЫЕ не отрабатывает из-за того, что данные кажутся одинаковыми только визуально. Проанализируем ситуацию подробнее. Строки могут содержать невидимые пробелы или отличаться регистром, а даты — секундами. Для системы "Иванов " и "Иванов" — это разные значения.
Чтобы исключить такие проблемы в запросе, рекомендуется:
СОКРЛП() (в коде) или убедиться, что в справочниках нет лишних пробелов.НАЧАЛОПЕРИОДА(Дата, ДЕНЬ).ПРЕДСТАВЛЕНИЕ() для ссылочных полей только в самом конце, так как строковые представления могут совпадать у разных объектов, что приведет к ложной уникальности.Применяя эти методы в совокупности, мы добьемся формирования чистой и корректной печатной формы без дублирования информации.