В системе 1С:Предприятие механизм выборки данных через запросы — один из ключевых инструментов для работы с информацией. Без понимания того, как работает выбор в запросе, невозможно эффективно разрабатывать отчёты, обрабатывать большие массивы данных или оптимизировать производительность базы. Однако многие разработчики сталкиваются с неочевидными нюансами: почему запрос возвращает не те данные, как избежать полного сканирования таблиц или почему простая выборка suddenly тормозит на больших объёмах?
Эта статья не просто перечислит синтаксис оператора ВЫБРАТЬ, а раскроет внутреннюю механику работы выборки в 1С: как формируется план выполнения, какие индексы задействуются, как влияет конфигурация СУБД (PostgreSQL, MS SQL, файловый вариант) и почему одни и те же запросы ведут себя по-разному в разных базах. Мы разберём реальные примеры кода, типичные ошибки и способы диагностики проблем — от простого ВЫБРАТЬ ПЕРВЫЕ 10 до сложных многотабличных соединений.
Базовый синтаксис: что происходит при выполнении ВЫБРАТЬ
Оператор ВЫБРАТЬ (или SELECT в англоязычной нотации) — основа любого запроса в 1С. На первый взгляд его конструкция проста:
```sql
ВЫБРАТЬ
Поле1, Поле2
ИЗ
Справочник.Товары КАК Товары
ГДЕ
Товары.Архивный = ЛОЖЬ
```
Но что на самом деле происходит, когда этот запрос выполняется?
- 🔹 Парсинг и валидация: платформа 1С проверяет синтаксическую корректность запроса, преобразует его во внутреннее представление (дерево запроса).
- 🔹 Оптимизация: анализируются условия (
ГДЕ), соединения (СОЕДИНИТЬ), агрегаты (ГРУППИРОВКА) для построения оптимального плана выполнения. - 🔹 Генерация SQL-кода: для клиент-серверного варианта запрос транслируется в SQL-запрос конкретной СУБД (например, в
SELECTдля MS SQL). - 🔹 Выполнение: СУБД обрабатывает запрос, используя индексы, статистику и другие механизмы.
- 🔹 Возврат результата: данные преобразуются обратно в таблицу значений 1С.
Критичный момент: в файловом варианте 1С запрос выполняется непосредственно движком платформы без трансляции в SQL, что ограничивает возможности оптимизации. Это объясняет, почему сложные запросы в файловой базе работают медленнее, чем в клиент-серверной.
Как 1С преобразует запрос в SQL: примеры и подводные камни
Рассмотрим, как простой запрос на языке 1С трансформируется в SQL для разных СУБД. Возьмём пример выборки документов за период:
```sql
ВЫБРАТЬ
Документ.Ссылка КАК Ссылка,
Документ.Дата КАК Дата
ИЗ
Документ.РеализацияТоваровУслуг КАК Документ
ГДЕ
Документ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
```
Для Microsoft SQL Server этот запрос может быть преобразован в:
```sql
SELECT
T1._Reference16 AS Ссылка,
T1._DateTime18 AS Дата
FROM
dbo._Document123 AS T1
WHERE
T1._DateTime18 BETWEEN @P1 AND @P2
```
А для PostgreSQL — немного иначе:
```sql
SELECT
"T1"."_Reference16" AS "Ссылка",
"T1"."_DateTime18" AS "Дата"
FROM
"_document123" AS "T1"
WHERE
"T1"."_DateTime18" BETWEEN $1 AND $2
```
Обратите внимание на ключевые отличия:
- 📌 Имена таблиц и полей: в SQL они заменяются на внутренние идентификаторы (например,
_Document123). - 📌 Параметры: вместо переменных 1С (
&НачалоПериода) подставляются параметры SQL (@P1или$1). - 📌 Типы данных: дата/время в 1С может транслироваться в разные SQL-типы (например,
datetimeилиtimestamp).
⚠️ Внимание: Если в запросе используются функции 1С (например, НАЧАЛОПЕРИОДА()), они не всегда корректно транслируются в SQL. В некоторых случаях платформа выгружает все данные и фильтрует их уже на стороне 1С, что критично для производительности.
Чтобы увидеть сгенерированный SQL-код, включите режим отладки в конфигураторе (Сервис → Параметры → Отладка → Показывать текст запроса) или используйте инструменты профилирования СУБД.
Индексы и планы выполнения: почему запрос тормозит
Одна из самых распространённых причин медленной работы запросов — неэффективное использование индексов. Даже если в таблице базы данных есть индекс по полю, 1С не всегда его задействует. Рассмотрим ключевые факторы:
| Фактор | Влияние на производительность | Пример |
|---|---|---|
| Тип сравнения | Равенство (=) использует индекс эффективнее, чем диапазоны (МЕЖДУ, >) |
ГДЕ Дата = &ТекущаяДата vs ГДЕ Дата >= &НачалоМесяца |
| Функции в условии | Функции над полем (например, НАЧСТР(Наименование)) блокируют использование индекса |
ГДЕ ЛЕВ(Артикул, 3) = "ABC" → полное сканирование |
| Соединения таблиц | Неправильный порядок соединений может привести к декартовым произведениям | СОЕДИНИТЬ Справочник.Контрагенты ПО Умолчанию → риск полного перебора |
| Агрегатные функции | СУММА() или КОЛИЧЕСТВО() без ГРУППИРОВКА требуют сканирования всех строк |
ВЫБРАТЬ СУММА(СуммаДокумента) → чтение всей таблицы |
Чтобы проанализировать план выполнения запроса в MS SQL Server, используйте:
```sql
SET SHOWPLAN_TEXT ON;
-- Ваш запрос
SET SHOWPLAN_TEXT OFF;
```
Для PostgreSQL подойдёт:
```sql
EXPLAIN ANALYZE
-- Ваш запрос
```
⚠️ Внимание: В клиент-серверном варианте 1С план выполнения формирует СУБД, а не платформа. Это означает, что для оптимизации нужно понимать особенности конкретной базы данных (например, в PostgreSQL и MS SQL могут использоваться разные стратегии индексирования).
Как просмотреть план выполнения в 1С 8.3
В конфигураторе откройте окно запроса, нажмите F5 (или кнопку "Выполнить"), затем перейдите на вкладку "План выполнения". Для детального анализа используйте инструменты СУБД (SQL Server Management Studio, pgAdmin и т.д.).
Типичные ошибки при написании запросов выборки
Даже опытные разработчики допускают ошибки, которые приводят к некорректным результатам или падению производительности. Разберём самые распространённые:
- 🚫 Игнорирование регистра: в некоторых СУБД (например, PostgreSQL) сравнение строк учитывает регистр, а в 1С — нет. Запрос
ГДЕ Наименование = "тОвАр"может не найти запись с названием "Товар". - 🚫 Неявные преобразования типов: сравнение даты со строкой (
ГДЕ Дата = "01.01.2023") приводит к полному сканированию таблицы. - 🚫 Избыточные соединения: соединение таблиц без явных условий (
СОЕДИНИТЬ Справочник.НоменклатурабезПО) создаёт декартово произведение. - 🚫 Неправильное использование
РАЗЛИЧНЫЕ: применениеВЫБРАТЬ РАЗЛИЧНЫЕко всем полям вместо конкретных (ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура.Ссылка) снижает производительность.
Пример опасной конструкции:
```sql
ВЫБРАТЬ
Документ.Номенклатура КАК Номенклатура,
Документ.Количество КАК Количество
ИЗ
Документ.РеализацияТоваровУслуг КАК Документ
ГДЕ
Документ.Номенклатура.Наименование = "Товар 1"
```
Здесь поле Наименование не индексируется, а условие применяется к связанной таблице (Справочник.Номенклатура), что приводит к последовательному чтению всех строк документа.
Указаны все необходимые поля в ВЫБРАТЬ (без *)|
Условия в ГДЕ используют индексируемые поля|
Нет функций над полями в условиях (ЛЕВ(), НАЧСТР())|
Соединения таблиц имеют явные условия (ПО)|
Для больших выборок ограничен результат (ПЕРВЫЕ 1000)
-->
Оптимизация запросов: практические советы
Ускорить выполнение запросов можно на нескольких уровнях: от корректировки кода до настройки СУБД. Вот ключевые рекомендации:
- Используйте явные соединения:
Заменяйте
СОЕДИНИТЬбез условий на явные связи:СОЕДИНИТЬ Справочник.Контрагенты КАК КонтрагентыПО Документ.Контрагент = Контрагенты.Ссылка
- Ограничивайте результат:
Для отладочных целей или предварительного просмотра используйте
ПЕРВЫЕ N:ВЫБРАТЬ ПЕРВЫЕ 100 - Избегайте подзапросов в условиях:
Подзапрос в
ГДЕчасто приводит к полному сканированию. Заменяйте его соединением:Плохо:
ГДЕ Номенклатура В (ВЫБРАТЬ Ссылка ИЗ Справочник.АкционныеТовары)Хорошо:
СОЕДИНИТЬ Справочник.АкционныеТовары ПО Номенклатура.Ссылка = АкционныеТовары.Ссылка - Анализируйте статистику:
В MS SQL Server обновите статистику командой
EXEC sp_updatestats, в PostgreSQL —ANALYZE.
Для сложных запросов с большим количеством соединений рассмотрите возможность денормализации данных — создания дополнительных реквизитов или регистров сведений, чтобы избежать многократных обращений к одним и тем же таблицам.
Самая частая причина медленных запросов — отсутствие индексов по полям, используемым в условиях ГДЕ и СОЕДИНИТЬ. Всегда проверяйте, есть ли индексы на ключевых полях (ссылки, даты, часто фильтруемые реквизиты).
Особенности выборки в разных версиях 1С
Механизм выборки данных эволюционировал вместе с платформой 1С:Предприятие. Рассмотрим ключевые различия между версиями:
| Версия платформы | Особенности выборки | Пример изменений |
|---|---|---|
| 1С 7.7 | Отсутствует язык запросов в современном виде. Выборка реализуется через объекты или внешние обработки. | Использование методов Выбрать(), НайтиПоНаименованию(). |
| 1С 8.0–8.1 | Появление языка запросов, но с ограниченными возможностями (нет ОБЪЕДИНИТЬ, ИТОГИ). |
Отсутствует поддержка ВРЕМЕННЫЕ ТАБЛИЦЫ. |
| 1С 8.2 | Добавлены временные таблицы, расширены возможности соединений. Появился ПАКЕТНЫЕ ЗАПРОСЫ. |
ВЫБРАТЬ ... В ВРЕМЕННУЮ ТАБЛИЦУ. |
| 1С 8.3 | Улучшена оптимизация запросов, добавлены новые функции (ВЫРАЗИТЬ, РАЗРЕШЕННЫЕ). |
ВЫБРАТЬ РАЗРЕШЕННЫЕ Номенклатура.Ссылка КАК Ссылка. |
| 1С 8.3.20+ | Поддержка JSON-функций, улучшенная работа с большими данными. |
ВЫБРАТЬ JSONЗНАЧЕНИЕ(Данные, '$.Сумма'). |
Важно учитывать, что в старых версиях (до 8.3.10) некоторые конструкции могут работать некорректно. Например, в 1С 8.2 запрос с ОБЪЕДИНИТЬ и ГРУППИРОВКА мог возвращать неверные итоги.
⚠️ Внимание: При переносе кода между версиями 1С всегда тестируйте запросы на целевой платформе. Например, функция ВЫРАЗИТЬ(), появившаяся в 8.3.10, вызовет ошибку в более ранних релизах.
Пакетные запросы и временные таблицы: когда и как использовать
Для сложных операций с данными в 1С предусмотрены пакетные запросы и временные таблицы. Они позволяют:
- 🔄 Разбивать большие выборки на части (пагинация).
- 🗃️ Сохранять промежуточные результаты для повторного использования.
- 🔗 Упрощать сложные соединения за счёт предварительной агрегации данных.
Пример использования временной таблицы для ускорения отчёта:
```sql
// Создаём временную таблицу с агрегированными данными
ВЫБРАТЬ
Документ.Контрагент КАК Контрагент,
СУММА(Документ.СуммаДокумента) КАК Итого
ИЗ
Документ.РеализацияТоваровУслуг КАК Документ
ГДЕ
Документ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
Документ.Контрагент
ПОМЕСТИТЬ ВТ_ИтогиПоКонтрагентам;
// Используем временную таблицу в основном запросе
ВЫБРАТЬ
Контрагенты.Наименование КАК Контрагент,
Итоги.Итого КАК Сумма
ИЗ
ВТ_ИтогиПоКонтрагентам КАК Итоги
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Итоги.Контрагент = Контрагенты.Ссылка
УПОРЯДОЧИТЬ ПО
Сумма УБЫВ
```
Пакетные запросы полезны, когда нужно выполнить несколько связанных операций в одной транзакции:
```sql
НАЧАТЬ ТРАНЗАКЦИЮ;
ВЫПОЛНИТЬ ЗАПРОС
"УДАЛИТЬ ИЗ ВТ_СтарыеДанные";
ВЫПОЛНИТЬ ЗАПРОС
"ВСТАВИТЬ В ВТ_НовыеДанные (Поле1, Поле2)
ВЫБРАТЬ ПолеА, ПолеБ ИЗ ИсточникДанных";
ЗАФИКСИРОВАТЬ ТРАНЗАКЦИЮ;
```
Ограничение: временные таблицы существуют только в рамках одного сеанса. После закрытия соединения или завершения транзакции (если не зафиксирована) данные будут утеряны.
FAQ: Частые вопросы о выборке в 1С
Почему запрос возвращает дублирующиеся строки?
Дубли возникают, если:
- В запросе нет условия
ГРУППИРОВКАдля агрегатных функций (СУММА,КОЛИЧЕСТВО). - Используются соединения
ПОЛНОЕилиЛЕВОЕбез фильтрацииNULL-значений. - В выборке участвуют таблицы с отношением "один-ко-многим" без явной агрегации.
Решение: добавьте РАЗЛИЧНЫЕ или ГРУППИРОВКА, проверьте логику соединений.
Как ускорить запрос с большим количеством соединений?
Способы оптимизации:
- Разбейте запрос на несколько этапов с использованием временных таблиц.
- Замените соединения
ПОЛНОЕнаЛЕВОЕилиВНУТРЕННЕЕ, если это возможно. - Добавьте индексы на поля, используемые в условиях
ГДЕиСОЕДИНИТЬ. - Используйте
ПЕРВЫЕ Nдля тестирования и отладки.
Можно ли в запросе 1С использовать подзапросы в разделе ВЫБРАТЬ?
Да, но с оговорками:
- Подзапросы в
ВЫБРАТЬдолжны возвращать одно значение (скалярные подзапросы). - Пример:
ВЫБРАТЬ (ВЫБРАТЬ МАКСИМУМ(Дата) ИЗ Документ.ЗаказыКлиентов) КАК ПоследняяДата. - Сложные подзапросы могут снизить производительность — лучше заменить их соединениями.
Почему запрос работает быстро в конфигураторе, но медленно в пользовательском режиме?
Возможные причины:
- В конфигураторе используется кэш метаданных, а в пользовательском режиме — нет.
- Разные права доступа: ограничения РЛС могут приводить к дополнительной фильтрации.
- В пользовательском режиме запрос выполняется в транзакции с другими операциями, что блокирует таблицы.
- На сервере 1С включены дополнительные проверки (например, аудит изменений).
Решение: проверьте план выполнения запроса в обоих режимах, анализируйте журнал регистрации.
Как выбрать данные из виртуальной таблицы регистров?
Виртуальные таблицы регистров (например, РегистрНакопления.ОстаткиТоваров) позволяют получать остатки, обороты и другие агрегированные данные без ручных расчётов. Пример:
ВЫБРАТЬ
ОстаткиТоваров.Номенклатура КАК Номенклатура,
ОстаткиТоваров.КоличествоОстаток КАК Остаток
ИЗ
РегистрНакопления.ОстаткиТоваров КАК ОстаткиТоваров
ГДЕ
ОстаткиТоваров.Номенклатура В (&СписокНоменклатуры)
И ОстаткиТоваров.Дата = &ТекущаяДата
Особенности:
- Виртуальные таблицы всегда возвращают актуальные данные на указанную дату.
- Для ускорения используйте отбор по ключевым полям (номенклатура, склад).
- Избегайте выборки всех полей (
*) — указывайте только необходимые.