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