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