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