Как правильно выбрать один вид цен в запросе 1С и избежать появления лишних данных

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При работе с ценообразованием в 1С:Предприятие часто возникает задача получить актуальные цены по конкретному виду цен (например, «Прайс-лист»), сопоставив их с ценами поставщиков. Однако при написании сложных запросов с использованием виртуальных таблиц и соединений программисты часто сталкиваются с тем, что в выборку попадают «лишние» записи или данные, не соответствующие установленному фильтру. В этой статье мы подробно разберем, почему это происходит и как правильно составить запрос для получения корректного результата.

Анализируем исходную ситуацию и проблему

Рассмотрим типовую ситуацию: у нас есть два регистра сведений — ЦеныНоменклатуры и ЦеныНоменклатурыПоставщиков. Нам нужно получить последние актуальные цены по конкретному виду цен из первого регистра и сравнить их с ценами из второго. Часто для этой цели используется временная таблица для определения «последнего периода» и последующее соединение.

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


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

Основная проблема здесь кроется в использовании ПОЛНОГО СОЕДИНЕНИЯ (FULL JOIN). Разберем по шагам, что делает система:

  1. Из таблицы собственных цен выбираются только те записи, где ВидЦены соответствует параметру &ПрайсЛист.
  2. Из таблицы цен поставщиков выбираются все записи (согласно условиям этого регистра).
  3. Система объединяет их. Если для какой-то номенклатуры нет цены «Прайс-лист», но есть цена поставщика, благодаря полному соединению эта строка все равно попадет в результат, но поля собственных цен будут содержать NULL.
  4. Функция ЕСТЬNULL в блоке ВЫБРАТЬ подставит номенклатуру из второй таблицы, и пользователю покажется, что фильтр по виду цены не сработал.

Шаг 1: Правильная фильтрация в параметрах виртуальной таблицы

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

Если мы хотим выбрать только один конкретный вид цен, наш первый пакет запроса (формирование временной таблицы периодов) должен выглядеть так:


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

Шаг 2: Выбор типа соединения

Для того чтобы в итоговом отчете были только те товары, по которым установлен вид цен «Прайс-лист», нам следует отказаться от ПОЛНОГО СОЕДИНЕНИЯ в пользу ЛЕВОГО СОЕДИНЕНИЯ (LEFT JOIN) или ВНУТРЕННЕГО СОЕДИНЕНИЯ.

Проанализируем ситуацию: если ваша цель — увидеть цены «Прайс-листа» и, если они есть, сравнить их с ценами поставщиков, используйте ЛЕВОЕ СОЕДИНЕНИЕ, где основной таблицей будет ЦеныНоменклатуры.

Посмотрим на пример правильного итогового запроса:


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

Шаг 3: Оптимизация работы с характеристиками

В запросах к ценам крайне важно учитывать Характеристика. Если в системе ведется учет по характеристикам, но для каких-то позиций они не заполнены, 1С хранит там значение ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка). При соединении таблиц через ЕСТЬNULL, как это было в исходном коде автора, может возникнуть путаница между NULL (отсутствие записи) и пустой ссылкой.

Рекомендация: Всегда включайте характеристики в условия соединения и группировки. Если характеристика не используется, соединение все равно отработает корректно по пустой ссылке, если вы не используете агрессивную фильтрацию ГДЕ Характеристика <> NULL.

Шаг 4: Использование ссылок вместо строк

Обратим внимание на важный аспект производительности и надежности. В исходном сообщении автор использовал фильтр по наименованию: ВидыЦен.Наименование = "БазоваяRUB". Это крайне нежелательная практика по нескольким причинам:

Рассмотрим, как передать параметр правильно в коде 1С:


Запрос.УстановитьПараметр("ПрайсЛист", Справочники.ВидыЦен.НайтиПоНаименованию("Прайс-Лист"));
// Или, если вид цены предопределенный:
Запрос.УстановитьПараметр("ПрайсЛист", Справочники.ВидыЦен.ОсновнойПрайс);

Подведем итоги

Для корректной выборки одного вида цен в сложном запросе необходимо соблюдать три правила:

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

Соблюдение этих принципов позволит вам получать точные данные и обеспечит высокую скорость работы отчетов даже на больших объемах данных.

← На главную