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