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