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