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