Как исправить ошибки в запросе к регистру бухгалтерии 1С и оптимизировать его работу?

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

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

Синтаксическая ошибка: экранирование кавычек в тексте запроса

Разберем ситуацию, с которой часто сталкиваются разработчики при переносе запросов из конфигуратора в Консоль запросов 1С и наоборот. Автор темы столкнулся с ошибкой при использовании конструкции СчетДт.Код = ""20.01"". Выясним причину этой проблемы.

Дело в том, что в платформе 1С существует два принципиально разных контекста написания запросов:

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

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ХозрасчетныйОборотыДтКт.СчетДт КАК СчетДт
    |ИЗ
    |	РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(,,, СчетДт.Код = ""20.01"") КАК ХозрасчетныйОборотыДтКт";

А теперь посмотрим на тот же запрос, подготовленный для работы непосредственно в Консоли запросов:


ВЫБРАТЬ
    ХозрасчетныйОборотыДтКт.СчетДт КАК СчетДт
ИЗ
    РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(,,, СчетДт.Код = "20.01") КАК ХозрасчетныйОборотыДтКт

Критическая ошибка производительности: Фильтрация в секции ГДЕ

Проанализируем исходный запрос автора темы. В нем условие по подразделению наложено в секции ГДЕ:

ГДЕ ХозрасчетныйОборотыДтКт.ПодразделениеДт = &Подразделение

Почему это является грубым нарушением стандартов разработки 1С?

Когда мы накладываем условие в секции ГДЕ на виртуальную таблицу, СУБД сначала выбирает из базы данных абсолютно все обороты по указанному счету за весь выбранный период по всем подразделениям организации, строит временную таблицу в оперативной памяти сервера, и только после этого осуществляет фильтрацию по подразделению. На реальных базах данных с миллионами проводок такой запрос гарантированно приведет к долгому ожиданию, блокировкам таблиц и перегрузке процессора сервера СУБД.

Как сделать правильно?

Любые отборы по измерениям или субконто необходимо передавать строго в параметры самой виртуальной таблицы. В случае с таблицей ОборотыДтКт фильтр по дебету должен накладываться в четвертом параметре — УсловиеДт. Перепишем запрос правильно:


ВЫБРАТЬ
    ХозрасчетныйОборотыДтКт.СчетДт,
    ХозрасчетныйОборотыДтКт.ПодразделениеДт,
    СУММА(ХозрасчетныйОборотыДтКт.СуммаОборот) КАК СуммаОборот
ИЗ
    РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(
        &НачалоПериода,
        &КонецПериода,
        ,
        СчетДт.Код = "20.01" И ПодразделениеДт = &Подразделение
    ) КАК ХозрасчетныйОборотыДтКт
СГРУППИРОВАТЬ ПО
    ХозрасчетныйОборотыДтКт.СчетДт,
    ХозрасчетныйОборотыДтКт.ПодразделениеДт

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

Оптимизация агрегатных функций: СУММА(ЕСТЬNULL()) против ЕСТЬNULL(СУММА())

В коде автора фигурирует следующая конструкция:

СУММА(ЕСТЬNULL(ХозрасчетныйОборотыДтКт.СуммаОборот, 0))

Рассмотрим подробнее, почему такое написание неоптимально с точки зрения СУБД. При вычислении функции СУММА(ЕСТЬNULL(Поле, 0)) сервер СУБД вынужден проверять на равенство NULL каждую детальную строку выборки до выполнения группировки. Если в выборке содержится 100 000 строк, функция ЕСТЬNULL выполнится ровно 100 000 раз.

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

ЕСТЬNULL(СУММА(ХозрасчетныйОборотыДтКт.СуммаОборот), 0)

При таком написании СУБД сначала быстро просуммирует значения (игнорируя значения NULL, что является стандартным поведением SQL), а проверка ЕСТЬNULL выполнится всего один раз — уже для результирующего сгруппированного значения.

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

Отказ от обращений через точку к коду счета

Использование конструкции СчетДт.Код = "20.01" крайне не рекомендуется разработчиками фирмы «1С». Проанализируем причины этого запрета:

Альтернативные варианты решения:

1. Использование конструкции ЗНАЧЕНИЕ() для обращения к предопределенному счету напрямую в базе данных (если счет является предопределенным):

СчетДт = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ОсновноеПроизводство)

2. Передача конкретного счета в качестве параметра запроса:

СчетДт = &ПараметрСчет

Оба метода позволяют СУБД сопоставлять данные напрямую по индексированному ссылочному полю, минуя лишние операции соединения таблиц.

Оптимальный выбор виртуальной таблицы: ОборотыДтКт против Обороты

В завершение разберем архитектурный выбор виртуальной таблицы. Автор темы использует РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт. Данная таблица является одной из самых «тяжелых» в системе, поскольку она сопоставляет дебетовые и кредитовые движения на уровне отдельных проводок.

Если перед нами стоит задача получить обороты дебета по счету 20.01 в разрезе подразделений без привязки к конкретным корреспондирующим счетам кредита, то использование ОборотыДтКт избыточно. Гораздо эффективнее использовать обычную таблицу Обороты:


ВЫБРАТЬ
    ХозрасчетныйОбороты.Счет КАК Счет,
    ХозрасчетныйОбороты.Субконто1 КАК Подразделение,
    ХозрасчетныйОбороты.СуммаОборотДт КАК СуммаОборот
ИЗ
    РегистрБухгалтерии.Хозрасчетный.Обороты(
        &НачалоПериода,
        &КонецПериода,
        ,
        Счет = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ОсновноеПроизводство),
        ,
        Субконто1 = &Подразделение
    ) КАК ХозрасчетныйОбороты

Такой запрос отработает в разы быстрее, так как таблица Обороты берет уже готовые рассчитанные итоги регистра бухгалтерии без необходимости выполнять сложное динамическое соединение дебетовых и кредитовых ветвей движений.

← На главную