Как добавить еще один вид цены в запрос 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При выгрузке прайс-листов, отчетов по ценам или заполнении табличных частей часто возникает необходимость получить в одном запросе несколько типов цен для позиций номенклатуры (например, оптовую и розничную или цены в разных валютах — рубли и евро). Рассмотрим, с какими ошибками можно столкнуться при решении этой задачи и как правильно доработать текст запроса.

Анализ исходной проблемы: ошибка с повторяющимся псевдонимом

Разберем типичную ситуацию. Предположим, у нас есть запрос, выбирающий данные справочника Номенклатура и цену из регистра сведений с видом цены "Розничная ЕВРО":


Запрос = Новый Запрос("ВЫБРАТЬ
    |    Номенклатура1.НаименованиеПолное,
    |    Номенклатура1.Артикул,
    |    Номенклатура1.Код,
    |    ЦеныНоменклатурыСрезПоследних.Цена,
    |    Номенклатура1.ЕдиницаИзмерения
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура1
    |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК ЦеныНоменклатурыСрезПоследних
    |        ПО Номенклатура1.Ссылка = ЦеныНоменклатурыСрезПоследних.Номенклатура
    |            И (ЦеныНоменклатурыСрезПоследних.ВидЦены.Наименование = ""Розничная ЕВРО"")
    |ГДЕ
    |    НЕ Номенклатура1.ПометкаУдаления
    |    И НЕ Номенклатура1.ЭтоГруппа");

Если попытаться добавив еще один блок ЛЕВОЕ СОЕДИНЕНИЕ с той же виртуальной таблицей среза последних без изменения имени источника, платформа 1С выдаст ошибку о том, что задан повторяющийся псевдоним. Платформа не сможет определить, из какого именно соединения браться полю Цена.

Решение 1. Использование уникальных псевдонимов для каждого соединения

Для исправления ошибки проанализируем секцию соединений запроса. Каждый присоединяемый источник данных (включая повторные обращения к одной и той же виртуальной таблице) должен иметь свой уникальный псевдоним, например ЦеныЕвро и ЦеныРубли (или ЦеныНоменклатурыСрезПоследних2).

Разберем пример с добавлением второго соединения и заданием уникального псевдонима:


ВЫБРАТЬ
    Номенклатура1.НаименованиеПолное КАК НаименованиеПолное,
    Номенклатура1.Артикул КАК Артикул,
    Номенклатура1.Код КАК Код,
    Номенклатура1.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    ЦеныЕвро.Цена КАК ЦенаЕвро,
    ЦеныРубли.Цена КАК ЦенаРубли
ИЗ
    Справочник.Номенклатура КАК Номенклатура1
    
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК ЦеныЕвро
        ПО Номенклатура1.Ссылка = ЦеныЕвро.Номенклатура
            И (ЦеныЕвро.ВидЦены.Наименование = "Розничная ЕВРО")
            
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК ЦеныРубли
        ПО Номенклатура1.Ссылка = ЦеныРубли.Номенклатура
            И (ЦеныРубли.ВидЦены.Наименование = "Розничная РУБЛИ")
ГДЕ
    НЕ Номенклатура1.ПометкаУдаления
    И НЕ Номенклатура1.ЭтоГруппа

Решение 2. Оптимизация запроса с передачей параметров и обработкой NULL

Хотя решение с уникальными псевдонимами устраняет синтаксическую ошибку, с точки зрения производительности и стандарта разработки 1С в нем есть слабые места:

  1. Фильтрация по наименованию ВидЦены.Наименование заставляет СУБД выполнять неявное соединение со справочником видов цен и искать данные по строке, что замедляет работу.
  2. Если отбор по виду цены указан в условии ПО, система рассчитывает срез последних по абсолютно всем видам цен в базе данных, а не только по нужным.
  3. Если у товара отсутствует цена одного из видов, запрос вернет значение NULL, что может вызвать ошибки при дальнейших расчетах.

Рассмотрим правильный подход: перенесем фильтр по виду цены в параметры виртуальной таблицы СрезПоследних, заменим поисковые строки на параметры-ссылки и обернем выбор цен в функцию ЕСТЬNULL.


ВЫБРАТЬ
    Номенклатура.НаименованиеПолное КАК НаименованиеПолное,
    Номенклатура.Артикул КАК Артикул,
    Номенклатура.Код КАК Код,
    Номенклатура.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    ЕСТЬNULL(ЦеныЕвро.Цена, 0) КАК ЦенаЕвро,
    ЕСТЬNULL(ЦеныРубли.Цена, 0) КАК ЦенаРубли
ИЗ
    Справочник.Номенклатура КАК Номенклатура
    
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
        &Период,
        ВидЦены = &ВидЦеныЕвро
    ) КАК ЦеныЕвро
        ПО Номенклатура.Ссылка = ЦеныЕвро.Номенклатура
        
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
        &Период,
        ВидЦены = &ВидЦеныРубли
    ) КАК ЦеныРубли
        ПО Номенклатура.Ссылка = ЦеныРубли.Номенклатура
ГДЕ
    НЕ Номенклатура.ПометкаУдаления
    И НЕ Номенклатура.ЭтоГруппа

Для передаваемых параметров в программном коде следует установить значения:


Запрос.УстановитьПараметр("Период", ТекущаяДатаСеанса());
Запрос.УстановитьПараметр("ВидЦеныЕвро", Справочники.ВидыЦен.НайтиПоНаименованию("Розничная ЕВРО"));
Запрос.УстановитьПараметр("ВидЦеныРубли", Справочники.ВидыЦен.НайтиПоНаименованию("Розничная РУБЛИ"));

Решение 3. Альтернативный способ через условие ВЫБОР и группировку

Посмотрим еще на один способ получения нескольких цен. Если видов цен много, большое количество левых соединений может негативно повлиять на скорость выполнения запроса. В такой ситуации мы можем применить конструкцию ВЫБОР КОГДА с последующей группировкой по номенклатуре:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    Номенклатура.НаименованиеПолное КАК НаименованиеПолное,
    Номенклатура.Артикул КАК Артикул,
    Номенклатура.Код КАК Код,
    МАКСИМУМ(ВЫБОР
        КОГДА Цены.ВидЦены = &ВидЦеныЕвро
            ТОГДА Цены.Цена
        ИНАЧЕ 0
    КОНЕЦ) КАК ЦенаЕвро,
    МАКСИМУМ(ВЫБОР
        КОГДА Цены.ВидЦены = &ВидЦеныРубли
            ТОГДА Цены.Цена
        ИНАЧЕ 0
    КОНЕЦ) КАК ЦенаРубли
ИЗ
    Справочник.Номенклатура КАК Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
        &Период,
        ВидЦены В (&МассивВидовЦен)
    ) КАК Цены
        ПО Номенклатура.Ссылка = Цены.Номенклатура
ГДЕ
    НЕ Номенклатура.ПометкаУдаления
    И НЕ Номенклатура.ЭтоГруппа
СГРУППИРОВАТЬ ПО
    Номенклатура.Ссылка,
    Номенклатура.НаименованиеПолное,
    Номенклатура.Артикул,
    Номенклатура.Код

В данном случае срез последних выполняется единожды по всем нужным видам цен, переданным в массиве &МассивВидовЦен, а значения раскладываются по колонкам с помощью агрегатной функции МАКСИМУМ и оператора ВЫБОР.

← На главную