Выборка данных — основа работы с любой базой в 1С:Предприятие. Без грамотного запроса невозможно построить отчет, сформировать документ или автоматизировать бизнес-процесс. Но как именно 1С обрабатывает выборки, почему одни запросы выполняются мгновенно, а другие «вешают» систему? В этой статье разберем внутреннюю механику выборки, от простого синтаксиса до тонкостей оптимизации, которые знают далеко не все разработчики.

Многие ошибочно считают, что выборка в — это просто «вытащить данные из таблицы». На деле это сложный процесс, задействующий индексы, планы выполнения и даже особенности СУБД (например, Microsoft SQL Server или PostgreSQL). Мы покажем, как транслирует ваш код в SQL, какие ошибки тормозят систему и как писать запросы, которые работают быстро даже на больших базах.

1. Что такое выборка в 1С: определение и ключевые понятия

В контексте 1С:Предприятие выборка — это процесс извлечения данных из базы по заданным критериям. Она может осуществляться:

  • 📌 Через встроенный язык (методы объектов, например Выбрать() для справочников)
  • 📌 Через запросы (конструкция Запрос с оператором ВЫБРАТЬ)
  • 📌 Через объектную модель (например, Документ.НайтиПоРеквизиту())

Главное отличие выборки от простого «чтения» данных — возможность фильтрации, сортировки и агрегации прямо в запросе. Например, вы можете одним запросом получить список товаров, которые продавались в текущем месяце, сгруппировать их по категориям и посчитать общую выручку.

📊 Какой способ выборки вы используете чаще?
Встроенный язык (Выбрать(), НайтиПоРеквизиту)
Запросы (ВЫБРАТЬ)
Объектная модель (Менеджеры объектов)
Не знаю, что это

Важно понимать, что любая выборка в так или иначе преобразуется в SQL-запрос к базе данных. Даже если вы используете объектные методы вроде Справочник.Товары.Выбрать(), платформа генерирует SQL «под капотом». Это означает, что производительность выборки зависит от:

  • 🔹 Правильности написания запроса
  • 🔹 Наличия индексов в базе данных
  • 🔹 Объема данных и структуры таблиц
💡

Используйте ПояснитьЗапрос() для объекта Запрос, чтобы увидеть, какой SQL-код сгенерировала 1С. Это поможет понять, почему запрос работает медленно.

2. Виды выборок в 1С: от простых к сложным

В существует несколько способов получить данные. Каждый из них имеет свои плюсы, минусы и области применения.

Тип выборки Пример кода Когда использовать Минусы
Прямой перебор объектов Для Каждого Товар Из Справочник.Товары Цикл ... КонецЦикла Небольшие справочники (до 1000 записей) Медленная работа на больших объемах, блокирует объекты
Методы менеджеров (Выбрать()) Выборка = Справочник.Товары.Выбрать(); Пока Выборка.Следующий() Цикл ... КонецЦикла Средние объемы данных, когда нужны дополнительные отборы Может быть медленнее запросов, не поддерживает сложные условия
Запросы (ВЫБРАТЬ) Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Товары.Наименование ИЗ Справочник.Товары КАК Товары";
Любые объемы данных, сложные отборы, агрегация Требует знания синтаксиса, возможны ошибки в условиях
Виртуальные таблицы ВЫБРАТЬ * ИЗ РегистрНакопления.ОстаткиТоваров.Остатки() Работа с регистрами, когда нужны остатки/обороты Могут быть ресурсоемкими, требуют понимания механизма регистров

Наиболее универсальным и производительным способом является использование запросов. Они позволяют:

  • 📊 Агрегировать данные (например, СУММА, КОЛИЧЕСТВО)
  • 🔍 Применять сложные условия отбора (вложенные запросы, СОЕДИНЕНИЕ)
  • 🔄 Объединять данные из разных таблиц (ЛЕВОЕ СОЕДИНЕНИЕ, ВНУТРЕННЕЕ СОЕДИНЕНИЕ)
💡

Запросы в 1С — это не просто «удобный синтаксис», а единственный способ эффективно работать с большими объемами данных. Даже если вы привыкли к объектным методам, для производительности критичных операций стоит освоить язык запросов.

3. Как 1С исполняет запрос: механика «под капотом»

Когда вы выполняете запрос в , происходит следующее:

  1. Парсинг и валидация. Платформа проверяет синтаксис запроса и преобразует его во внутреннее представление.
  2. Генерация SQL. Запрос транслируется в SQL-код, понятный СУБД (например, MS SQL или PostgreSQL).
  3. Оптимизация плана выполнения. СУБД анализирует, как лучше выполнить запрос: какие индексы использовать, в каком порядке соединять таблицы.
  4. Выполнение. СУБД извлекает данные и возвращает их в .
  5. Форматирование результата. Платформа преобразует данные в привычный для 1С формат (например, РезультатЗапроса).

Ключевой момент — план выполнения. Именно он определяет, будет ли запрос быстрым или медленным. Например, если в запросе нет условий по индексированным полям, СУБД может решить просканировать всю таблицу (table scan), что крайне неэффективно.

Что такое индексы в базе данных?

Индексы — это специальные структуры, которые ускоряют поиск данных по определенным полям. Например, если у вас есть индекс по полю Артикул в справочнике товаров, запрос ГДЕ Артикул = "ABC123" будет выполняться мгновенно, даже если в справочнике миллион записей. Без индекса СУБД пришлось бы перебрать все записи подряд.

Чтобы увидеть, как именно исполняет ваш запрос, используйте:

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 10 Товары.Наименование ИЗ Справочник.Товары КАК Товары";

ПланЗапроса = Запрос.Выполнить().ПолучитьПланВыполнения();

⚠️ Внимание: Планы выполнения могут отличаться в зависимости от версии СУБД и настроек сервера. То, что быстро работает на тестовой базе с 1000 записями, может тормозить на боевой базе с миллионами строк. Всегда тестируйте производительность на реальных данных!

4. Синтаксис запросов: основные конструкции с примерами

Язык запросов внешне похож на 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С:Предприятие нет встроенного инструмента для просмотра индексов, но вы можете:

  1. Запустить SQL Server Management Studio (или аналогичный инструмент для вашей СУБД) и посмотреть индексы в таблицах базы данных .
  2. Использовать запрос к системным таблицам СУБД. Например, для MS SQL:
    SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID('dbo._Reference123')

    (где _Reference123 — имя таблицы справочника в базе).

  3. В конфигураторе посмотреть свойства реквизитов — индексированные поля помечены соответствующей иконкой.

Обратите внимание, что автоматически создает индексы для некоторых полей (например, Ссылка, ПометкаУдаления), но для пользовательских реквизитов индексы нужно добавлять вручную.

❓ Можно ли в одном запросе обновить данные?

Да, в языке запросов есть оператор ОБНОВИТЬ, но он имеет ограничения:

  • 🔹 Можно обновлять только реквизиты объектов (не сами объекты).
  • 🔹 Нельзя обновлять данные в регистрах напрямую (нужно использовать объектную модель).
  • 🔹 Оператор ОБНОВИТЬ не поддерживает соединения таблиц.

Пример:

Запрос = Новый Запрос;

Запрос.Текст =

"ОБНОВИТЬ Справочник.Товары

УСТАНОВИТЬ Цена = Цена * 1.1

ГДЕ Категория = &Категория";

Для сложных обновлений лучше использовать Для Каждого ... Из с объектной моделью.

❓ Как сделать выборку с постраничным выводом?

Для постраничного вывода (пагинации) используйте конструкцию ПЕРВЫЕ с параметром ПРОПУСТИТЬ:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

Товары.Ссылка КАК Ссылка,

Товары.Наименование КАК Наименование

ИЗ

Справочник.Товары КАК Товары

УПОРЯДОЧИТЬ ПО

Товары.Наименование

ПЕРВЫЕ &РазмерСтраницы ПРОПУСТИТЬ &НомерСтраницы * &РазмерСтраницы";

Где:

  • &РазмерСтраницы — количество записей на странице (например, 50).
  • &НомерСтраницы — текущая страница (начинается с 0).

Важно: Для корректной работы пагинации всегда указывайте УПОРЯДОЧИТЬ ПО, иначе при пропуске записей могут возникать дубли или пропуски.

❓ Чем отличается ВНУТРЕННЕЕ СОЕДИНЕНИЕ от ЛЕВОЕ СОЕДИНЕНИЕ?

ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN) возвращает только те записи, для которых есть совпадения в обеих таблицах. ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) возвращает все записи из левой таблицы, а для правой — NULL, если совпадений нет.

Пример с ВНУТРЕННИМ СОЕДИНЕНИЕМ:

ВЫБРАТЬ

Заказы.Номер,

Товары.Наименование

ИЗ

Документ.ЗаказПокупателя КАК Заказы

ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя.Товары КАК СтрокиЗаказа

ПО Заказы.Ссылка = СтрокиЗаказа.Ссылка

ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Товары КАК Товары

ПО СтрокиЗаказа.Товар = Товары.Ссылка

Этот запрос вернет только те заказы, в которых есть строки с товарами (и товары не удалены).

Пример с ЛЕВЫМ СОЕДИНЕНИЕМ:

ВЫБРАТЬ

Заказы.Номер,

ЕСТЬNULL(Товары.Наименование, "") КАК НаименованиеТовара

ИЗ

Документ.ЗаказПокупателя КАК Заказы

ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя.Товары КАК СтрокиЗаказа

ПО Заказы.Ссылка = СтрокиЗаказа.Ссылка

ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Товары КАК Товары

ПО СтрокиЗаказа.Товар = Товары.Ссылка

Этот запрос вернет все заказы, даже если в них нет строк с товарами (или товары удалены). Для отсутствующих товаров поле НаименованиеТовара будет пустым.