Как отобрать документы определенного вида из регистра сведений и просуммировать значения их свойств

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

Работа с механизмом дополнительных свойств (характеристик) в 1С — это стандартная задача для любого разработчика. Однако она таит в себе множество нюансов, связанных с типами данных и производительностью запросов. Часто мы сталкиваемся с регистром сведений, где измерение Документ имеет составной тип, а ресурс Значение — тип Характеристика. В этой статье мы подробно разберем, как правильно фильтровать такие данные, суммировать их и избегать типичных ошибок при обработке пустых значений.

Разбор проблемы: почему не работает простой фильтр и сумма

Рассмотрим ситуацию: у нас есть регистр СвойстваДокументов. Одно из его измерений — Документ (составной тип), а ресурс — Значение (тип Характеристика.ДополнительныеПоля). Нам нужно получить сумму значений определенного свойства только для документов вида Инцидент.

Первая проблема, с которой мы сталкиваемся — попытка суммировать поле с типом Характеристика. В языке запросов 1С функция СУММА работает только с числовыми типами. Поскольку поле Значение может хранить и строку, и дату, и ссылку, SQL-сервер «не знает», как складывать эти данные. Вызов функции СУММА(Значение) приведет к ошибке еще на этапе проверки запроса.

Вторая проблема — фильтрация по виду документа. Использование конструкции ССЫЛКА является корректным, но требует понимания того, как платформа обрабатывает обращения к полям через точку от составного типа.

Шаг 1: Фильтрация по виду документа в составном поле

Для начала проанализируем, как отобрать записи, относящиеся только к конкретному метаданному. В системе 1С для этого используется оператор ССЫЛКА. Рассмотрим пример правильного условия в секции ГДЕ:


ВЫБРАТЬ
    СвойстваДокументов.Документ КАК Документ,
    СвойстваДокументов.Значение КАК Значение
ИЗ
    РегистрСведений.СвойстваДокументов КАК СвойстваДокументов
ГДЕ
    СвойстваДокументов.Документ ССЫЛКА Документ.Инцидент
    И СвойстваДокументов.Свойство = &Свойство

Важное замечание по производительности: когда мы обращаемся к СвойстваДокументов.Документ.Контрагент, система выполняет неявное соединение (LEFT JOIN) со всеми таблицами, входящими в составной тип измерения Документ. Если в типе указано 50 видов документов, это может существенно замедлить запрос. Чтобы оптимизировать работу, рекомендуется сначала использовать оператор ВЫРАЗИТЬ, который «подскажет» системе, к какой конкретной таблице нужно обращаться.

Шаг 2: Преобразование характеристики в число для суммирования

Чтобы получить возможность складывать значения, хранящиеся в ресурсе Значение, нам необходимо явно привести их к числовому типу. Для этого мы используем функцию ВЫРАЗИТЬ. Выясним причину, по которой это критично: функция ВЫРАЗИТЬ превращает неопределенный тип в конкретный (например, ЧИСЛО). Если в поле окажется значение другого типа (например, ссылка или строка), результатом выражения будет NULL, что безопасно для функции СУММА.

Рассмотрим структуру запроса с использованием временной таблицы для подготовки данных:


ВЫБРАТЬ
    СвойстваДокументов.Документ КАК Документ,
    // Принудительно приводим характеристику к числу
    ВЫРАЗИТЬ(СвойстваДокументов.Значение КАК ЧИСЛО(15, 2)) КАК ЧисловоеЗначение,
    СвойстваДокументов.Документ.Клиент КАК Клиент
ПОМЕСТИТЬ ВТ_ПодготовленныеДанные
ИЗ
    РегистрСведений.СвойстваДокументов КАК СвойстваДокументов
ГДЕ
    СвойстваДокументов.Документ ССЫЛКА Документ.Инцидент
    И СвойстваДокументов.Свойство = &Свойство
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ПодготовленныеДанные.Клиент КАК Клиент,
    СУММА(ВТ_ПодготовленныеДанные.ЧисловоеЗначение) КАК ИтоговаяСумма
ИЗ
    ВТ_ПодготовленныеДанные КАК ВТ_ПодготовленныеДанные
СГРУППИРОВАТЬ ПО
    ВТ_ПодготовленныеДанные.Клиент

В данном примере мы сначала «очистили» данные во временной таблице, превратив характеристики в числа, а затем во втором запросе применили агрегатную функцию СУММА с группировкой по клиенту.

Шаг 3: Обработка NULL и конфликты типов

Часто при выводе отчетов мы хотим заменить пустое значение (NULL) на какой-то понятный текст, например, прочерки "--". Здесь кроется ловушка. Если вы используете ЕСТЬNULL(Документ.Клиент, "--"), то колонка в результате запроса становится составного типа: СправочникСсылка.Клиенты + Строка.

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

  1. Использование пустой ссылки: Вместо строки "--" лучше подставлять пустую ссылку того же типа. Это сохранит типизацию колонки.
    
    ЕСТЬNULL(СвойстваДокументов.Документ.Клиент, ЗНАЧЕНИЕ(Справочник.Клиенты.ПустаяСсылка))
    
  2. Оформление на уровне вывода: Если вам нужно именно визуальное представление в виде прочерков, лучше оставить в запросе NULL, а замену производить уже при выводе в отчет (например, в Системе Компоновки Данных через вкладку "Условное оформление").
  3. Обработка через ВЫБОР: Если замена на строку все же необходима для специфических целей, используйте конструкцию ВЫБОР КОГДА ... ТОГДА, но помните о потере типизации.

Шаг 4: Оптимизация «точечной» нотации

Посмотрим на пример запроса, который может работать крайне медленно: СвойстваДокументов.Документ.Клиент.Контрагент.Наименование. Каждая точка — это соединение с новой таблицей. В данном случае их три. Если в регистре миллион записей, запрос будет выполняться очень долго.

Разберем, как сделать правильно:

  1. Сначала отберите во временную таблицу только ссылки на нужные документы: ВЫБРАТЬ Документ ПОМЕСТИТЬ ВТ_Докс ....
  2. Затем соедините эту временную таблицу с таблицей самого документа, чтобы получить Клиента.
  3. Затем соедините результат с таблицей Клиента, чтобы получить Контрагента.

Такой подход позволяет СУБД построить оптимальный план запроса и использовать индексы максимально эффективно.

Итоговый пример эффективного запроса

Соберем все наши знания в один работающий и быстрый механизм:


ВЫБРАТЬ
    СвойстваДокументов.Документ КАК Документ,
    ВЫРАЗИТЬ(СвойстваДокументов.Значение КАК ЧИСЛО(15, 2)) КАК СогласованнаяСумма,
    // Используем ВЫРАЗИТЬ для оптимизации обращения к реквизитам
    ВЫРАЗИТЬ(СвойстваДокументов.Документ КАК Документ.Инцидент).Клиент КАК Клиент
ПОМЕСТИТЬ ВТ_Свойства
ИЗ
    РегистрСведений.СвойстваДокументов КАК СвойстваДокументов
ГДЕ
    СвойстваДокументов.Документ ССЫЛКА Документ.Инцидент
    И СвойстваДокументов.Свойство = &Свойство
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Свойства.Клиент КАК Клиент,
    // Получаем наименование через одно соединение
    ЕСТЬNULL(ВТ_Свойства.Клиент.Наименование, "Не указан") КАК КлиентНаименование,
    СУММА(ВТ_Свойства.СогласованнаяСумма) КАК Итого
ИЗ
    ВТ_Свойства КАК ВТ_Свойства
СГРУППИРОВАТЬ ПО
    ВТ_Свойства.Клиент,
    ВТ_Свойства.Клиент.Наименование

В этом решении мы применили типизацию через ВЫРАЗИТЬ, подготовили числовые данные для агрегации и вынесли расчеты во временную таблицу, что обеспечивает стабильную работу даже на больших объемах данных. Помните, что работа с характеристиками всегда требует явного указания типов, если вы планируете выполнять математические операции над их значениями.

← На главную