Выборка данных — основа работы с любой базой в 1С:Предприятие. Без грамотного запроса невозможно построить отчет, сформировать документ или автоматизировать бизнес-процесс. Но как именно 1С обрабатывает выборки, почему одни запросы выполняются мгновенно, а другие «вешают» систему? В этой статье разберем внутреннюю механику выборки, от простого синтаксиса до тонкостей оптимизации, которые знают далеко не все разработчики.
Многие ошибочно считают, что выборка в 1С — это просто «вытащить данные из таблицы». На деле это сложный процесс, задействующий индексы, планы выполнения и даже особенности СУБД (например, Microsoft SQL Server или PostgreSQL). Мы покажем, как 1С транслирует ваш код в SQL, какие ошибки тормозят систему и как писать запросы, которые работают быстро даже на больших базах.
1. Что такое выборка в 1С: определение и ключевые понятия
В контексте 1С:Предприятие выборка — это процесс извлечения данных из базы по заданным критериям. Она может осуществляться:
- 📌 Через встроенный язык (методы объектов, например
Выбрать()для справочников) - 📌 Через запросы (конструкция
Запросс операторомВЫБРАТЬ) - 📌 Через объектную модель (например,
Документ.НайтиПоРеквизиту())
Главное отличие выборки от простого «чтения» данных — возможность фильтрации, сортировки и агрегации прямо в запросе. Например, вы можете одним запросом получить список товаров, которые продавались в текущем месяце, сгруппировать их по категориям и посчитать общую выручку.
Важно понимать, что любая выборка в 1С так или иначе преобразуется в SQL-запрос к базе данных. Даже если вы используете объектные методы вроде Справочник.Товары.Выбрать(), платформа 1С генерирует SQL «под капотом». Это означает, что производительность выборки зависит от:
- 🔹 Правильности написания запроса
- 🔹 Наличия индексов в базе данных
- 🔹 Объема данных и структуры таблиц
Используйте ПояснитьЗапрос() для объекта Запрос, чтобы увидеть, какой SQL-код сгенерировала 1С. Это поможет понять, почему запрос работает медленно.
2. Виды выборок в 1С: от простых к сложным
В 1С существует несколько способов получить данные. Каждый из них имеет свои плюсы, минусы и области применения.
| Тип выборки | Пример кода | Когда использовать | Минусы |
|---|---|---|---|
| Прямой перебор объектов | Для Каждого Товар Из Справочник.Товары Цикл ... КонецЦикла |
Небольшие справочники (до 1000 записей) | Медленная работа на больших объемах, блокирует объекты |
Методы менеджеров (Выбрать()) |
Выборка = Справочник.Товары.Выбрать(); Пока Выборка.Следующий() Цикл ... КонецЦикла |
Средние объемы данных, когда нужны дополнительные отборы | Может быть медленнее запросов, не поддерживает сложные условия |
Запросы (ВЫБРАТЬ) |
Запрос = Новый Запрос; |
Любые объемы данных, сложные отборы, агрегация | Требует знания синтаксиса, возможны ошибки в условиях |
| Виртуальные таблицы | ВЫБРАТЬ * ИЗ РегистрНакопления.ОстаткиТоваров.Остатки() |
Работа с регистрами, когда нужны остатки/обороты | Могут быть ресурсоемкими, требуют понимания механизма регистров |
Наиболее универсальным и производительным способом является использование запросов. Они позволяют:
- 📊 Агрегировать данные (например,
СУММА,КОЛИЧЕСТВО) - 🔍 Применять сложные условия отбора (вложенные запросы,
СОЕДИНЕНИЕ) - 🔄 Объединять данные из разных таблиц (
ЛЕВОЕ СОЕДИНЕНИЕ,ВНУТРЕННЕЕ СОЕДИНЕНИЕ)
Запросы в 1С — это не просто «удобный синтаксис», а единственный способ эффективно работать с большими объемами данных. Даже если вы привыкли к объектным методам, для производительности критичных операций стоит освоить язык запросов.
3. Как 1С исполняет запрос: механика «под капотом»
Когда вы выполняете запрос в 1С, происходит следующее:
- Парсинг и валидация. Платформа проверяет синтаксис запроса и преобразует его во внутреннее представление.
- Генерация SQL. Запрос транслируется в SQL-код, понятный СУБД (например, MS SQL или PostgreSQL).
- Оптимизация плана выполнения. СУБД анализирует, как лучше выполнить запрос: какие индексы использовать, в каком порядке соединять таблицы.
- Выполнение. СУБД извлекает данные и возвращает их в 1С.
- Форматирование результата. Платформа преобразует данные в привычный для 1С формат (например,
РезультатЗапроса).
Ключевой момент — план выполнения. Именно он определяет, будет ли запрос быстрым или медленным. Например, если в запросе нет условий по индексированным полям, СУБД может решить просканировать всю таблицу (table scan), что крайне неэффективно.
Что такое индексы в базе данных?
Индексы — это специальные структуры, которые ускоряют поиск данных по определенным полям. Например, если у вас есть индекс по полю Артикул в справочнике товаров, запрос ГДЕ Артикул = "ABC123" будет выполняться мгновенно, даже если в справочнике миллион записей. Без индекса СУБД пришлось бы перебрать все записи подряд.
Чтобы увидеть, как именно 1С исполняет ваш запрос, используйте:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 10 Товары.Наименование ИЗ Справочник.Товары КАК Товары";
ПланЗапроса = Запрос.Выполнить().ПолучитьПланВыполнения();
⚠️ Внимание: Планы выполнения могут отличаться в зависимости от версии СУБД и настроек сервера. То, что быстро работает на тестовой базе с 1000 записями, может тормозить на боевой базе с миллионами строк. Всегда тестируйте производительность на реальных данных!
4. Синтаксис запросов: основные конструкции с примерами
Язык запросов 1С внешне похож на SQL, но имеет свои особенности. Рассмотрим ключевые конструкции:
4.1. Простая выборка
Базовый запрос для извлечения данных:
ВЫБРАТЬ
Товары.Наименование КАК Наименование,
Товары.Артикул КАК Артикул
ИЗ
Справочник.Товары КАК Товары
ГДЕ
Товары.ПометкаУдаления = ЛОЖЬ
4.2. Фильтрация (ГДЕ)
Отбор данных по условиям:
ВЫБРАТЬ
Документ.ЗаказПокупателя.Номер КАК Номер,
Документ.ЗаказПокупателя.Дата КАК Дата
ИЗ
Документ.ЗаказПокупателя КАК Документ.ЗаказПокупателя
ГДЕ
Документ.ЗаказПокупателя.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И Документ.ЗаказПокупателя.Контрагент = &Контрагент
4.3. Агрегация (ГРУППИРОВКА)
Группировка данных с подсчетом итогов:
ВЫБРАТЬ
Товары.Категория КАК Категория,
СУММА(Товары.Цена) КАК ОбщаяСтоимость,
КОЛИЧЕСТВО(*) КАК КоличествоТоваров
ИЗ
Справочник.Товары КАК Товары
ГДЕ
Товары.ПометкаУдаления = ЛОЖЬ
СГРУППИРОВАТЬ ПО
Товары.Категория
4.4. Соединение таблиц (СОЕДИНЕНИЕ)
Объединение данных из нескольких таблиц:
ВЫБРАТЬ
Заказы.Номер КАК НомерЗаказа,
Товары.Наименование КАК Товар,
СтрокиЗаказа.Количество КАК Количество
ИЗ
Документ.ЗаказПокупателя КАК Заказы
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя.Товары КАК СтрокиЗаказа
ПО Заказы.Ссылка = СтрокиЗаказа.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Товары КАК Товары
ПО СтрокиЗаказа.Товар = Товары.Ссылка
ГДЕ
Заказы.Дата = &ДатаЗаказа
Указаны все необходимые поля в SELECT|Добавлены условия отбора (WHERE)|Правильно расставлены скобки в условиях|Параметры запроса (&Параметр) инициализированы перед выполнением|Проверен план выполнения на больших данных-->
5. Типичные ошибки и как их избежать
Даже опытные разработчики иногда допускают ошибки, которые ведут к медленной работе запросов. Вот самые распространенные:
- 🚫 Отсутствие индексов. Запрос по неиндексированному полю приводит к полному сканированию таблицы. Например, поиск по
Наименованиебез индекса будет медленным. - 🚫 Избыточные данные в SELECT. Запрос
ВЫБРАТЬ *извлекает все поля, даже ненужные. Указывайте только те, которые действительно требуются. - 🚫 Сложные условия в WHERE. Условия с функциями (например,
ЛЕВ(Наименование, 3) = "ABC") не могут использовать индексы. - 🚫 Неправильные соединения.
ПОЛНОЕ СОЕДИНЕНИЕилиПРАВОЕ СОЕДИНЕНИЕчасто ведут к неожиданным результатам и низкой производительности.
Пример неэффективного запроса:
ВЫБРАТЬ *
ИЗ Документ.РеализацияТоваров КАК Реализация
ГДЕ НАЧИНАЕТСЯС(Реализация.Номер, "РТ-")
Проблемы:
- 🔸
ВЫБРАТЬ *— избыточные данные - 🔸 Функция
НАЧИНАЕТСЯСв условии — индекс не используется
Исправленный вариант:
ВЫБРАТЬ
Реализация.Ссылка,
Реализация.Дата,
Реализация.Контрагент
ИЗ
Документ.РеализацияТоваров КАК Реализация
ГДЕ
Реализация.Номер ПОДОБНО "РТ-%"
⚠️ Внимание: Если вы используете 1С:Предприятие 8.3.20+, обратите внимание на новый оператор РАЗРЕШЕННЫЕ для работы с правами доступа в запросах. Его отсутствие может привести к ошибкам при выполнении запроса пользователями с ограниченными правами.
6. Оптимизация выборок: практические советы
Чтобы запросы работали быстро, следуйте этим рекомендациям:
6.1. Используйте индексы
Добавьте индексы для полей, по которым часто идет поиск. Например, для справочника Товары целесообразно проиндексировать:
- 📌
Артикул - 📌
Штрихкод - 📌
Категория
6.2. Ограничивайте объем данных
Используйте ПЕРВЫЕ N или РАЗЛИЧНЫЕ, если не нужны все записи:
ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 100
Товары.Категория
ИЗ
Справочник.Товары КАК Товары
6.3. Избегайте вложенных запросов
Вместо:
ВЫБРАТЬ Товары.Наименование
ИЗ Справочник.Товары КАК Товары
ГДЕ Товары.Категория В (
ВЫБРАТЬ Категории.Ссылка ИЗ Справочник.КатегорииТоваров КАК Категории ГДЕ Категории.Активна
)
Лучше использовать соединение:
ВЫБРАТЬ Товары.Наименование
ИЗ Справочник.Товары КАК Товары
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.КатегорииТоваров КАК Категории
ПО Товары.Категория = Категории.Ссылка
ГДЕ Категории.Активна
6.4. Кэшируйте результаты
Если одни и те же данные запрашиваются многократно, сохраняйте их в кэш (например, в Соответствие или Массив):
Процедура ПолучитьТоварыНаСкладе(Склад)
Если НЕ ЗначениеЗаполнено(КэшТоваров) Тогда
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Товары.Ссылка, Товары.Наименование ИЗ Справочник.Товары КАК Товары";
КэшТоваров = Запрос.Выполнить().Выгрузить();
КонецЕсли;
Возврат КэшТоваров.Найти(Склад, "Склад");
КонецПроцедуры
Оптимизация запросов — это не разовое действие, а процесс. После изменений в базе (например, добавления новых данных) планы выполнения могут «портиться». Регулярно проверяйте производительность критичных запросов!
7. Работа с большими данными: виртуальные таблицы и временные таблицы
Когда речь идет о миллионах записей, обычные запросы могут не справиться. В таких случаях используют:
7.1. Виртуальные таблицы
Они позволяют получить данные из регистров без явного перебора. Например, для получения остатков товаров:
ВЫБРАТЬ
ОстаткиТоваров.Товар КАК Товар,
ОстаткиТоваров.КоличествоОстаток КАК Остаток
ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки(&ТекущаяДата) КАК ОстаткиТоваров
7.2. Временные таблицы
Если нужно выполнить сложную обработку данных, разбейте задачу на этапы с использованием временных таблиц:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
Товары.Ссылка КАК Ссылка,
Товары.Наименование КАК Наименование
ПОМЕСТИТЬ ВТТовары
ИЗ
Справочник.Товары КАК Товары;
//////////////////////////////////
ВЫБРАТЬ
Продажи.Товар КАК Товар,
СУММА(Продажи.Количество) КАК Продано
ИЗ
Документ.РеализацияТоваров.Товары КАК Продажи
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТТовары КАК Товары
ПО Продажи.Товар = Товары.Ссылка
ГРУППИРОВАТЬ ПО
Продажи.Товар";
Временные таблицы (ПОМЕСТИТЬ ВТ...) позволяют:
- 📌 Разбить сложный запрос на простые части
- 📌 Уменьшить нагрузку на СУБД
- 📌 Повторно использовать промежуточные результаты
⚠️ Внимание: Виртуальные таблицы регистров (например,Остатки()илиОбороты()) могут быть очень ресурсоемкими. Не используйте их в циклах или без ограничений по периодам. Всегда указывайте конкретную дату или диапазон!
FAQ: Частые вопросы о выборке в 1С
❓ Почему запрос работает медленно, хотя данных мало?
Причины могут быть разные:
- 🔹 Отсутствуют индексы по полям в условии
ГДЕ. - 🔹 В запросе используются функции (например,
ЛЕВ(),НАЧИНАЕТСЯС()), которые блокируют использование индексов. - 🔹 СУБД выбрала неоптимальный план выполнения (проверьте через
ПояснитьЗапрос()). - 🔹 Запрос извлекает лишние данные (например,
ВЫБРАТЬ *вместо конкретных полей).
Используйте ПланЗапроса = Запрос.Выполнить().ПолучитьПланВыполнения(), чтобы анализировать проблемные места.
❓ Как узнать, какие индексы есть в базе?
В 1С:Предприятие нет встроенного инструмента для просмотра индексов, но вы можете:
- Запустить SQL Server Management Studio (или аналогичный инструмент для вашей СУБД) и посмотреть индексы в таблицах базы данных 1С.
- Использовать запрос к системным таблицам СУБД. Например, для MS SQL:
SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID('dbo._Reference123')(где
_Reference123— имя таблицы справочника в базе). - В конфигураторе посмотреть свойства реквизитов — индексированные поля помечены соответствующей иконкой.
Обратите внимание, что 1С автоматически создает индексы для некоторых полей (например, Ссылка, ПометкаУдаления), но для пользовательских реквизитов индексы нужно добавлять вручную.
❓ Можно ли в одном запросе обновить данные?
Да, в языке запросов 1С есть оператор ОБНОВИТЬ, но он имеет ограничения:
- 🔹 Можно обновлять только реквизиты объектов (не сами объекты).
- 🔹 Нельзя обновлять данные в регистрах напрямую (нужно использовать объектную модель).
- 🔹 Оператор
ОБНОВИТЬне поддерживает соединения таблиц.
Пример:
Запрос = Новый Запрос;
Запрос.Текст =
"ОБНОВИТЬ Справочник.Товары
УСТАНОВИТЬ Цена = Цена * 1.1
ГДЕ Категория = &Категория";
Для сложных обновлений лучше использовать Для Каждого ... Из с объектной моделью.
❓ Как сделать выборку с постраничным выводом?
Для постраничного вывода (пагинации) используйте конструкцию ПЕРВЫЕ с параметром ПРОПУСТИТЬ:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
Товары.Ссылка КАК Ссылка,
Товары.Наименование КАК Наименование
ИЗ
Справочник.Товары КАК Товары
УПОРЯДОЧИТЬ ПО
Товары.Наименование
ПЕРВЫЕ &РазмерСтраницы ПРОПУСТИТЬ &НомерСтраницы * &РазмерСтраницы";
Где:
&РазмерСтраницы— количество записей на странице (например, 50).&НомерСтраницы— текущая страница (начинается с 0).
Важно: Для корректной работы пагинации всегда указывайте УПОРЯДОЧИТЬ ПО, иначе при пропуске записей могут возникать дубли или пропуски.
❓ Чем отличается ВНУТРЕННЕЕ СОЕДИНЕНИЕ от ЛЕВОЕ СОЕДИНЕНИЕ?
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN) возвращает только те записи, для которых есть совпадения в обеих таблицах. ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) возвращает все записи из левой таблицы, а для правой — NULL, если совпадений нет.
Пример с ВНУТРЕННИМ СОЕДИНЕНИЕМ:
ВЫБРАТЬ
Заказы.Номер,
Товары.Наименование
ИЗ
Документ.ЗаказПокупателя КАК Заказы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя.Товары КАК СтрокиЗаказа
ПО Заказы.Ссылка = СтрокиЗаказа.Ссылка
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Товары КАК Товары
ПО СтрокиЗаказа.Товар = Товары.Ссылка
Этот запрос вернет только те заказы, в которых есть строки с товарами (и товары не удалены).
Пример с ЛЕВЫМ СОЕДИНЕНИЕМ:
ВЫБРАТЬ
Заказы.Номер,
ЕСТЬNULL(Товары.Наименование, "") КАК НаименованиеТовара
ИЗ
Документ.ЗаказПокупателя КАК Заказы
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя.Товары КАК СтрокиЗаказа
ПО Заказы.Ссылка = СтрокиЗаказа.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Товары КАК Товары
ПО СтрокиЗаказа.Товар = Товары.Ссылка
Этот запрос вернет все заказы, даже если в них нет строк с товарами (или товары удалены). Для отсутствующих товаров поле НаименованиеТовара будет пустым.