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

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

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

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

Метод 1: Использование условия в секции ГДЕ

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

Рассмотрим этот способ на примере. Допустим, нам нужно получить сумму неустойки по документу ЛК_Претензия, исключив первую строку:


ВЫБРАТЬ
    СУММА(ЛК_Претензия.Неустойка) КАК Неустойка
ИЗ
    Документ.ЛК_Претензия.ОсновныеДанные КАК ЛК_Претензия
ГДЕ
    ЛК_Претензия.Ссылка = &Ссылка
    И ЛК_Претензия.НомерСтроки <> 1

Преимущества данного метода:

  1. Производительность: СУБД отфильтровывает записи до начала выполнения математических операций.
  2. Простота: Код остается лаконичным и легко читаемым.

Важное ограничение: Если ваш запрос выбирает данные сразу по нескольким документам (как в исходном вопросе на форуме), условие ЛК_Претензия.НомерСтроки <> 1 исключит первую строку из каждого документа, попавшего в выборку. Если ваша задача — исключить только одну-единственную «самую первую» строку во всем наборе данных, этот метод потребует доработки через временные таблицы.

Метод 2: Условное суммирование с оператором ВЫБОР

Если нам необходимо в одном запросе получить и полную сумму, и сумму без первой строки, или если логика исключения более сложная, мы можем использовать оператор ВЫБОР внутри агрегатной функции СУММА. Это позволяет гибко управлять тем, какие значения участвуют в расчете.

Разберем вариант, предложенный участниками обсуждения:


ВЫБРАТЬ
    СУММА(ВЫБОР 
        КОГДА ЛК_Претензия.НомерСтроки = 1 ТОГДА 0 
        ИНАЧЕ ЛК_Претензия.Неустойка 
    КОНЕЦ) КАК НеустойкаБезПервойСтроки
ИЗ
    Документ.ЛК_Претензия.ОсновныеДанные КАК ЛК_Претензия
ГДЕ
    ЛК_Претензия.Ссылка.Контрагент = &Контрагент

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


ВЫБРАТЬ
    СУММА(ЛК_Претензия.Неустойка) - 
    СУММА(ВЫБОР 
        КОГДА ЛК_Претензия.НомерСтроки = 1 ТОГДА ЛК_Претензия.Неустойка 
        ИНАЧЕ 0 
    КОНЕЦ) КАК ИтоговаяСумма
ИЗ
    Документ.ЛК_Претензия.ОсновныеДанные КАК ЛК_Претензия

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

Метод 3: Обработка NULL при вычитании

При выполнении арифметических операций в запросах крайне важно помнить о значениях NULL. Если в результате фильтрации или отсутствия данных в табличной части запрос вернет NULL, любая попытка вычитания из этого значения приведет к тому, что весь результат станет NULL.

Рекомендуем всегда использовать функцию ЕСТЬNULL для обеспечения стабильности расчетов:


ВЫБРАТЬ
    ЕСТЬNULL(СУММА(ЛК_Претензия.Неустойка), 0) - 
    ЕСТЬNULL(СУММА(ВЫБОР 
        КОГДА ЛК_Претензия.НомерСтроки = 1 ТОГДА ЛК_Претензия.Неустойка 
        ИНАЧЕ 0 
    КОНЕЦ), 0) КАК Результат

Метод 4: Использование оконных функций (для платформы 8.3.21+)

Если вы работаете на современной платформе (начиная с версии 8.3.21), вы можете использовать мощный инструмент — оконные функции. Это особенно полезно, когда «первая строка» определяется не физическим порядком в табличной части (НомерСтроки), а, например, самой ранней датой или минимальной ценой внутри группы строк.

Рассмотрим, как пронумеровать строки динамически и исключить первую:


ВЫБРАТЬ
    ВТ_Данные.Неустойка КАК Неустойка
ИЗ (
    ВЫБРАТЬ
        ЛК_Претензия.Неустойка КАК Неустойка,
        НОМЕР_СТРОКИ() OVER (ПОРЯДОК ПО ЛК_Претензия.Дата) КАК ВнутреннийНомер
    ИЗ
        Документ.ЛК_Претензия.ОсновныеДанные КАК ЛК_Претензия
) КАК ВТ_Данные
ГДЕ
    ВТ_Данные.ВнутреннийНомер > 1

Этот метод позволяет гибко определять, какая именно строка будет считаться «первой», независимо от того, как они были введены пользователем в интерфейсе.

Сравнение методов и рекомендации

Проанализируем ситуацию, какой метод выбрать в зависимости от ваших целей:

  1. Если нужно просто исключить строку из суммы: Используйте Метод 1 (условие ГДЕ). Это самый быстрый способ.
  2. Если нужно считать несколько сумм (с учетом первой строки и без): Используйте Метод 2 (оператор ВЫБОР).
  3. Если "первая строка" — это не обязательно №1: Используйте оконные функции или временные таблицы с поиском минимального значения (например, минимальной даты).
  4. Если расчет идет по нескольким документам: Помните, что НомерСтроки = 1 будет у каждого документа. Если нужно исключить только одну строку из всего массива документов, используйте вложенные запросы для определения глобального идентификатора этой строки.

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

← На главную