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