Как получить одно значение из табличной части документа с помощью запроса 1С?

Программист 1С v8.3 (Управляемые формы) Сфера услуг, туризм и социальный сектор
← На главную

Работа с табличными частями в запросах 1С часто вызывает трудности у начинающих разработчиков. Типичная ситуация: вам нужно вытянуть всего одно число (например, количество часов по определенной дисциплине), но запрос упорно возвращает структуру, где внутри одной таблицы находится другая таблица. В этой статье мы разберем, почему так происходит, и как написать эффективный и лаконичный запрос для решения этой задачи.

Проблема вложенных таблиц в результате запроса

Рассмотрим ситуацию, с которой столкнулся автор темы. При попытке выбрать данные из табличной части документа через обращение к объекту (например, Документ.ПриказЗачисление.УчебныйПлан.Дисциплины.(КоличествоЧасов)), система 1С формирует результат, в котором поле «Дисциплины» само является объектом типа РезультатЗапроса. Чтобы достать оттуда значение, программисту приходится писать вложенные циклы в коде 1С, что крайне неудобно и непроизводительно.

Рассмотрим, почему это плохо:

  1. Производительность: Для каждой строки основного запроса СУБД вынуждена подготавливать отдельный набор данных. Это создает колоссальную нагрузку на память сервера.
  2. Сложность обработки: Вместо прямого получения значения в переменную, вам нужно обходить выборку, затем получать еще одну выборку из колонки и только потом обращаться к данным.
  3. Трудночитаемый код: Такие запросы сложно отлаживать и поддерживать.

Разбор неэффективного решения

Проанализируем пример «извращенного» запроса, к которому сначала пришел автор. В нем используются множественные вложенные запросы в секции ИЗ:


Запрос.Текст = "ВЫБРАТЬ
               |    УчебныйПланДисциплины.КоличествоЧасов КАК КоличествоЧасов
               |ИЗ
               |    Документ.УчебныйПлан.Дисциплины КАК УчебныйПланДисциплины,
               |    (ВЫБРАТЬ
               |        УчебныйПлан.Дисциплины.(...)
               |    ИЗ
               |        Документ.УчебныйПлан КАК УчебныйПлан) КАК ВложенныйЗапрос";

Такой подход избыточен. Здесь происходит соединение табличной части самой с собой через вложенные конструкции. Мы видим типичную ошибку: попытку применить логику объектного программирования (где мы идем «вглубь» объекта) к реляционным таблицам БД.

Оптимальное решение: Прямое обращение к табличной части

Для того чтобы получить конкретное значение из строк табличной части, нам не нужно выбирать сам документ. Мы можем обратиться напрямую к таблице этой части. В системе 1С любая табличная часть любого документа или справочника представлена в базе данных как отдельная физическая таблица.

Разберем по шагам правильный алгоритм построения запроса:

  1. В качестве источника данных в секции ИЗ выбираем не сам документ, а его табличную часть: Документ.УчебныйПлан.Дисциплины.
  2. Устанавливаем фильтры в секции ГДЕ, используя разыменование полей через точку (например, Ссылка.УчебныйГод).
  3. Если нам нужно гарантированно одно значение, используем конструкцию ПЕРВЫЕ 1.

Посмотрим на пример правильного кода:


ВЫБРАТЬ ПЕРВЫЕ 1
    УчебныйПланДисциплины.КоличествоЧасов КАК КоличествоЧасов
ИЗ
    Документ.УчебныйПлан.Дисциплины КАК УчебныйПланДисциплины
ГДЕ
    УчебныйПланДисциплины.Дисциплина = &Дисциплина
    И УчебныйПланДисциплины.ПериодКонтроля = &ПериодКонтроля
    И УчебныйПланДисциплины.Ссылка.УчебныйГод = &УчебныйГод
    И УчебныйПланДисциплины.Ссылка.Специальность = &Специальность
    И УчебныйПланДисциплины.Ссылка В (
        ВЫБРАТЬ
            ПриказЗачисление.УчебныйПлан
        ИЗ
            Документ.ПриказЗачисление КАК ПриказЗачисление
        ГДЕ
            ПриказЗачисление.Группа = &Группа
    )

Важные нюансы и рекомендации

1. Использование «точечной нотации» (Разыменование)

В примере выше мы используем конструкцию УчебныйПланДисциплины.Ссылка.УчебныйГод. Важно понимать, что каждое обращение через точку к реквизиту ссылки порождает неявное левое соединение (LEFT JOIN) в SQL-запросе. Если у вас очень много таких обращений, это может замедлить работу. В простых случаях, как наш, это допустимо и делает код чище.

2. Конструкция ПЕРВЫЕ 1

Если ваша логика предполагает, что в результате запроса должна быть ровно одна строка, всегда добавляйте ПЕРВЫЕ 1. Это оптимизирует работу СУБД: как только сервер найдет первую подходящую запись, он прекратит поиск и вернет результат.

3. Обработка результата в коде 1С

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


РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

// Инициализируем переменную значением по умолчанию
КолЧас = 0; 

Если Выборка.Следующий() Тогда
    КолЧас = Выборка.КоличествоЧасов;
КонецЕсли;

Возврат КолЧас;

Продвинутый уровень: Использование временных таблиц

Проанализируем ситуацию, если условий фильтрации слишком много или связи становятся сложными. В таких случаях вложенные запросы в секции ГДЕ (как В (ВЫБРАТЬ...) в нашем примере) могут работать медленно. Профессионалы 1С рекомендуют использовать временные таблицы.

Схема работы с временной таблицей:

  1. Сначала выбираем ссылки на нужные Учебные планы из Приказов во временную таблицу.
  2. Индексируем эту таблицу.
  3. Во втором запросе соединяем табличную часть с этой временной таблицей.

Это позволяет оптимизатору SQL построить более эффективный план выполнения запроса. Для простых разовых выборок это может быть избыточно, но для отчетов или массовых обработок это стандарт индустрии.

Заключение

Подведем итоги. Чтобы вынуть значение из табличной части и не запутаться в «матрешках» из таблиц:

  1. Всегда делайте выборку из самой таблицы табличной части (Документ.Имя.ИмяТЧ).
  2. Используйте параметры для фильтрации данных (&Дисциплина, &Группа).
  3. Применяйте ПЕРВЫЕ 1, чтобы получить единственное значение.
  4. Обрабатывайте результат через Если Выборка.Следующий(), чтобы избежать ошибок при пустом результате.

Следуя этим правилам, вы обеспечите высокую скорость работы системы и чистоту вашего программного кода.

← На главную