Разработка сложных алгоритмов в платформе 1С:Предприятие часто требует манипуляций с временными наборами данных, которые не хранятся в базе данных физически. Для этих целей идеально подходит объект ТаблицаЗначений, позволяющий хранить данные в оперативной памяти. Однако, когда возникает необходимость отфильтровать, сгруппировать или соединить эти данные с информацией из регистров, обычный перебор строк цикла становится неэффективным.
Именно здесь на сцену выходит механизм запросов. Многие разработчики ошибочно полагают, что язык запросов 1С работает исключительно с таблицами базы данных, но это не так. Вы можете выполнить полноценный ВЫБРАТЬ непосредственно из переменной типа ТаблицаЗначений. Это открывает мощные возможности для обработки данных без лишних обращений к СУБД.
В этой статье мы детально разберем синтаксис, нюансы использования псевдонимов и типичные ошибки, которые допускают новички при работе с выборками из таблиц значений. Понимание этих механизмов позволит вам писать более чистый и производительный код.
Синтаксис запроса к временному набору данных
Основное отличие запроса к таблице значений от запроса к реальной таблице базы данных заключается в источнике данных. Вместо имени физической таблицы в секции ИЗ вы указываете имя переменной, содержащей объект ТаблицаЗначений. Синтаксически это выглядит предельно просто, но требует соблюдения определенных правил именования.
Рассмотрим базовый пример. Допустим, у нас есть таблица значений ТЗ_Товары, заполненная номенклатурой и количествами. Чтобы получить список только тех товаров, количество которых превышает 10 единиц, мы напишем следующий код:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ_Товары.Номенклатура,
| ТЗ_Товары.Количество
|ИЗ
| ТЗ_Товары КАК ТЗ_Товары
|ГДЕ
| ТЗ_Товары.Количество > 10";
Запрос.УстановитьПараметр("ТЗ_Товары", ТЗ_Товары);
Результат = Запрос.Выполнить();
Обратите внимание на ключевое слово КАК. Оно обязательно для задания псевдонима таблицы, даже если псевдоним совпадает с именем переменной. Без явного указания псевдонима компилятор запросов выдаст ошибку синтаксиса. Это фундаментальное требование платформы при работе с временными наборами.
⚠️ Внимание: Переменная с таблицей значений должна быть установлена в параметры запроса до момента выполнения метода
Выполнить(). Если вы забудете вызватьУстановитьПараметр, система не сможет найти источник данных и прервет выполнение с ошибкой "Неопределенная таблица".
Всегда давайте понятные имена псевдонимам в запросах. Избегайте имен вроде Т1, Т2, Т3 — они делают код нечитаемым при отладке сложных выборок.
Работа с полями и типами данных
Структура полей в запросе полностью соответствует колонкам, добавленным в таблицу значений. Однако стоит помнить о строгой типизации 1С. Если вы добавляли колонку как тип Число, то в запросе к ней применимы математические операции. Если колонка имеет тип Строка, сравнения будут выполняться лексикографически.
Частая проблема возникает при попытке выбрать поля, которые не были добавлены в таблицу на этапе её создания. В отличие от запросов к базе данных, где можно использовать функции преобразования типов "на лету", в таблице значений вы ограничены её схемой. Вы не можете динамически добавить поле ВЫРАЖЕНИЕ(Строка(Артикул)) прямо в тексте запроса без предварительной подготовки данных.
Для решения задач сложной фильтрации часто используют составные типы. Например, если в одной колонке хранятся ссылки на разные справочники, запрос корректно обработает их, но при выборке конкретных реквизитов этих ссылок могут возникнуть нюансы. Всегда проверяйте, что типы данных в условиях ГДЕ совместимы.
- 📌 Используйте метод
Колонки.Добавить()с явным указанием типа перед заполнением таблицы. - 📌 При выборке дат убедитесь, что в таблице значений поле имеет тип Дата, а не Строка.
- 📌 Для булевых значений используйте сравнение с истиной:
ГДЕ Пометка = ИСТИНА.
Фильтрация и условия отбора (ГДЕ)
Секция ГДЕ в запросе к таблице значений работает абсолютно аналогично обычным запросам. Вы можете использовать логические операторы И, ИЛИ, а также скобки для группировки условий. Это позволяет реализовать сложную бизнес-логику отбора данных без написания громоздких циклов Для Каждого.
Особое внимание стоит уделить параметризации условий. Жесткое прописывание значений в тексте запроса (например, ГДЕ Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.Товар1)) допустимо, но менее гибко. Гораздо эффективнее передавать значения через параметры запроса. Это не только ускоряет компиляцию плана выполнения, но и защищает код от ошибок экранирования специальных символов.
Рассмотрим пример фильтрации по списку значений. Если вам нужно отобрать строки, где номенклатура входит в определенный перечень, лучше передать этот перечень как вторую таблицу значений и использовать оператор В с подзапросом или соединение.
Запрос.Текст =
"ВЫБРАТЬ
| ОсновнаяТЗ.Номенклатура
|ИЗ
| ОсновнаяТЗ КАК ОсновнаяТЗ
|ГДЕ
| ОсновнаяТЗ.Номенклатура В
| (ВЫБРАТЬ
| ФильтрТЗ.Номенклатура
| ИЗ
| ФильтрТЗ КАК ФильтрТЗ)";
⚠️ Внимание: Оператор
ПОДОБНОработает с таблицами значений медленнее, чем операторы точного сравнения=илиВ. Если объем данных в таблице превышает несколько тысяч строк, старайтесь избегать поиска по маске в больших массивах памяти.
Оптимизация условий ГДЕ
Если вы фильтруете по дате, всегда старайтесь использовать диапазон (МЕЖДУ), а не функции преобразования даты, так как это позволяет движку запросов использовать более эффективные алгоритмы сравнения.
Группировка и агрегатные функции
Одним из главных преимуществ использования запроса к таблице значений является возможность быстрой агрегации данных. Функции СУММА, КОЛИЧЕСТВО, МИНИМУМ, МАКСИМУМ и СРЕДНЕЕ работают с временными наборами так же быстро, как и с таблицами БД. Это идеальный инструмент для построения итоговых отчетов в памяти.
При использовании группировки необходимо явно перечислить все неагрегированные поля в секции ГРУППИРОВАТЬ ПО. Забудьте о правиле "все, что не в функциях, автоматически группируется" — в 1С это работает только при явном указании. Ошибка в списке группировки приведет к тому, что запрос просто не выполнится.
Часто возникает необходимость отфильтровать результаты уже после группировки. Для этого предназначена секция ИМЕЮЩИЕ. Она работает аналогично ГДЕ, но применяется к уже сгруппированным данным. Например, если нужно показать только те категории товаров, общая сумма которых превышает миллион рублей.
| Функция | Описание | Пример использования |
|---|---|---|
СУММА() |
Суммирует значения числового поля | СУММА(ТЗ.Сумма) |
КОЛИЧЕСТВО() |
Подсчитывает количество строк | КОЛИЧЕСТВО(ТЗ.Номенклатура) |
МАКСИМУМ() |
Находит наибольшее значение | МАКСИМУМ(ТЗ.ДатаДокумента) |
МИНИМУМ() |
Находит наименьшее значение | МИНИМУМ(ТЗ.Цена) |
Использование агрегатных функций в запросе к Таблице Значений всегда быстрее, чем ручной цикл суммирования, особенно на больших объемах данных (более 10 000 строк).
Соединение таблиц значений (ЛЕВОЕ СОЕДИНЕНИЕ)
Продвинутым сценарием является соединение двух и более таблиц значений между собой или соединение таблицы значений с реальной таблицей базы данных. Синтаксис ЛЕВОЕ СОЕДИНЕНИЕ, ВНУТРЕННЕЕ СОЕДИНЕНИЕ поддерживается в полном объеме. Это позволяет обогащать временные данные справочной информацией без лишних проходов по базе.
Представьте ситуацию: вы выгрузили данные из внешней системы в таблицу значений, и теперь вам нужно подставить к ним актуальные наименования контрагентов из базы 1С. Вы можете выполнить соединение вашей ТЗ_Выгрузка со справочником Контрагенты прямо в тексте запроса.
Запрос.Текст =
"ВЫБРАТЬ
| Выгрузка.КодКонтрагента,
| Контрагенты.Наименование КАК ПолноеНаименование
|ИЗ
| Выгрузка КАК Выгрузка
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
| ПО Выгрузка.КодКонтрагента = Контрагенты.Код";
Важно помнить о производительности таких операций. Если таблица значений очень велика, а соединение идет с большой таблицей БД, время выполнения может возрасти. В таких случаях иногда эффективнее сначала записать таблицу значений во временную таблицу базы данных (ВРЕМЕННАЯ ТАБЛИЦА), а затем работать с ней.
- 🚀 ЛЕВОЕ СОЕДИНЕНИЕ сохранит все строки из левой таблицы, даже если совпадений нет.
- 🚀 ВНУТРЕННЕЕ СОЕДИНЕНИЕ оставит только строки, где есть совпадение в обеих таблицах.
- 🚀 Убедитесь, что типы полей в условии
ПОстрого совпадают, иначе соединение не сработает.
⚠️ Внимание: При соединении Таблицы Значений с таблицей БД убедитесь, что индексация полей в базе данных настроена корректно. Отсутствие индекса по полю соединения может привести к полному сканированию таблицы и серьезному замедлению работы.
☑️ Проверка перед соединением таблиц
Частые ошибки и способы их решения
Несмотря на простоту синтаксиса, разработчики часто наступают на одни и те же грабли. Самая распространенная ошибка — попытка обратиться к таблице значений, которая была очищена или пересоздана после установки параметра. Помните: параметр запроса хранит ссылку на объект. Если вы сделаете ТЗ = Новый ТаблицаЗначений после установки параметра, запрос будет работать со старой, уже неактуальной таблицей.
Еще один подводный камень — именование колонок. Если в таблице значений есть колонка с пробелом в имени (например, № Строки), в запросе её обязательно нужно заключать в квадратные скобки: [№ Строки]. Иначе парсер запросов воспримет это как два разных идентификатора и выдаст ошибку синтаксиса.
Также стоит упомянуть проблему с NULL-значениями. В 1С пустое значение ссылки и значение НЕОПРЕДЕЛЕНО обрабатываются специфично. При фильтрации условий ГДЕ Поле = ЗНАЧЕНИЕ(...) строки с неопределенными значениями могут неожиданно исключаться из выборки, если не использовать оператор ЕСТЬ NULL.
Для диагностики проблем используйте метод Запрос.ТекстЗапроса (в отладчике) или выводите текст запроса в журнал регистрации. Это позволяет увидеть итоговый текст, который уходит на исполнение, и найти несоответствия в именах полей.
Лайфхак для отладки
Если запрос не выполняется, попробуйте скопировать его текст в Консоль Запросов (если она установлена) и выполнить там, подставив тестовые данные. Это часто быстрее, чем запускать всю обработку целиком.
FAQ: Вопросы и ответы
Можно ли изменить данные в таблице значений через запрос?
Нет, язык запросов 1С предназначен только для чтения данных (операция SELECT). Вы не можете выполнить UPDATE или DELETE непосредственно в тексте запроса к Таблице Значений. Для изменения данных необходимо получить результат запроса и изменить его в цикле, либо пересоздать таблицу.
Какой максимальный размер может иметь Таблица Значений для запроса?
Технического ограничения на количество строк нет, оно ограничено лишь доступной оперативной памятью сервера или клиента. Однако при объемах свыше 100-200 тысяч строк производительность запроса может снижаться, и целесообразнее использовать временные таблицы в базе данных.
Как передать таблицу значений из клиента на сервер для запроса?
Если код выполняется на сервере, а таблица заполнена на клиенте, передайте её как параметр вызова серверной процедуры. Объект ТаблицаЗначений сериализуется автоматически при передаче между контекстами выполнения.
Работает ли ПОРЯДОК в запросе к таблице значений?
Да, секция УПОРЯДОЧИТЬ ПО полностью поддерживается. Вы можете сортировать данные по любому полю таблицы значений, указывать направление сортировки (ВОЗР / УБЫ) и использовать несколько полей для многоступенчатой сортировки.