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