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