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