В практике разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда виртуальная таблица СрезПоследних регистра сведений возвращает не одну «последнюю» запись, а целую выборку строк для одного и того же объекта. Особенно часто это проявляется в конфигурациях 1С:ERP 2.5, 1С:Управление торговлей 11.5 или 1С:Комплексная автоматизация 2.5 при работе с регистром ЦеныНоменклатуры25. Разберем по шагам, почему так происходит, и как составить запрос правильно.
Прежде всего, нам необходимо вспомнить фундаментальный принцип работы регистров сведений. Виртуальная таблица СрезПоследних возвращает последние записи в разрезе уникальных комбинаций всех измерений регистра. Если в регистре три измерения (например, Номенклатура, ВидЦены, Характеристика), то система вернет последнюю актуальную запись для каждой уникальной связки этих трех полей.
Проанализируем типичную ошибку: если мы просим у системы «последнюю цену для товара Абразив», но не указываем конкретный вид цены или характеристику, 1С вернет нам:
В итоге вместо одной ожидаемой цифры мы получаем таблицу. Именно это и произошло в рассматриваемой ситуации на форуме.
Одной из главных ошибок начинающих разработчиков является использование секции ГДЕ для фильтрации данных виртуальной таблицы. Рассмотрим пример некорректного кода:
ВЫБРАТЬ
Цены.Цена,
Цены.Номенклатура
ИЗ
РегистрСведений.ЦеныНоменклатуры25.СрезПоследних(&Период, ) КАК Цены
ГДЕ
Цены.Номенклатура = &Номенклатура
И Цены.ВидЦены = &ВидЦены
Почему это работает плохо? В данном случае платформа может сначала получить срез последних цен вообще по всей базе на указанную дату, а уже потом отсечь лишнее по условию ГДЕ. Это не только замедляет работу, но и при определенных условиях соединения таблиц может приводить к дублям.
Рассмотрим, как сделать правильно. Параметры фильтрации нужно передавать внутрь скобок виртуальной таблицы. Это заставляет SQL-сервер сразу выбирать только нужные записи, что гарантирует корректность результата.
ВЫБРАТЬ
Цены.Цена,
Цены.Номенклатура,
Цены.ВидЦены
ИЗ
РегистрСведений.ЦеныНоменклатуры25.СрезПоследних(&Период,
Номенклатура = &Номенклатура
И ВидЦены = &ВидЦены
) КАК Цены
Если даже после установки параметров в скобках вы продолжаете получать несколько строк, значит, записи отличаются каким-то еще измерением, которое вы не учли. Проанализируем состав регистра ЦеныНоменклатуры25. Помимо очевидных полей, там могут быть:
Неопределено.Чтобы выяснить причину, выполним отладочный запрос, выбрав абсолютно все поля регистра без группировок:
ВЫБРАТЬ
*
ИЗ
РегистрСведений.ЦеныНоменклатуры25.СрезПоследних(&Период, Номенклатура = &Номенклатура) КАК Цены
Просмотрев результат в консоли запросов, мы увидим, в каких колонках значения различаются. Если мы видим две строки с одинаковым видом цены, но в одной колонка Характеристика пустая, а в другой заполнена — вот причина «дублей».
Иногда возникают ситуации, когда в одну и ту же секунду (период) были проведены два документа установки цен. Платформа 1С при получении СрезаПоследних по дате (типа ДатаВремя) может вернуть обе записи, если не может однозначно определить приоритет.
Для решения этой проблемы при получении данных в коде 1С:Предприятие рекомендуется использовать не просто дату, а МоментВремени или Граница. В запросах же стоит обращать внимание на Период и при необходимости использовать функции МАКСИМУМ, если логика допускает получение любой из последних цен, установленных в одно время.
В первом сообщении темы автор использовал сложное соединение с временной таблицей ВТМаксимальныеДаты по условию Регистратор.Дата. Это крайне неэффективный и опасный путь.
Проанализируем причину: обращение к полю Регистратор.Дата через точку в запросе вызывает неявное соединение с таблицами всех документов, которые могут быть регистраторами для этого регистра (Установка цен, Поступление товаров и т.д.). Это колоссально снижает производительность.
Выясним, как оптимизировать такой запрос. Если вам нужно получить цену для списка номенклатуры, лучше сначала подготовить временную таблицу с товарами, а затем соединить её с виртуальной таблицей СрезПоследних, передав список товаров в параметры:
// Сначала помещаем товары в ВТ_Товары
// ...
ВЫБРАТЬ
Товары.Номенклатура,
Цены.Цена
ИЗ
ВТ_Товары КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры25.СрезПоследних(&Период,
Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТ_Товары)
И ВидЦены = &ВидЦены
) КАК Цены
ПО Товары.Номенклатура = Цены.Номенклатура
Если вы получаете «непонятную» цену 1500 вместо 1300, или ваш запрос возвращает несколько строк для одного товара, выполните следующие действия:
Номенклатуре, но и по ВидуЦены.Характеристик или Упаковок.*) из среза последних, чтобы увидеть, чем отличаются строки-дубли.Период в регистре сведений и дата документа-регистратора — это не всегда одно и то же. Используйте штатные механизмы виртуальных таблиц.Следуя этим правилам, вы обеспечите стабильную и быструю работу ваших отчетов и механизмов ценообразования в 1С.