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