Запросы к регистрам бухгалтерии в платформе «1С:Предприятие» являются одними из самых ресурсоемких операций. Даже незначительная синтаксическая или методологическая ошибка может привести к колоссальному падению производительности системы или аварийному завершению работы запроса. В этой статье мы подробно разберем практический кейс исправления ошибок в запросе к виртуальной таблице ОборотыДтКт, проанализируем правила работы с кавычками, а также рассмотрим ключевые стандарты оптимизации СУБД, которые позволят вашим запросам работать максимально быстро.
Разберем ситуацию, с которой часто сталкиваются разработчики при переносе запросов из конфигуратора в Консоль запросов 1С и наоборот. Автор темы столкнулся с ошибкой при использовании конструкции СчетДт.Код = ""20.01"". Выясним причину этой проблемы.
Дело в том, что в платформе 1С существует два принципиально разных контекста написания запросов:
""20.01"" в консоли вызовет синтаксическую ошибку (ожидается символ закрывающей скобки), так как парсер воспримет две кавычки подряд как пустую строку, после которой идет непонятный текст. В консоли запросов нужно использовать строго одиночные кавычки: "20.01".Посмотрим на пример правильного написания в коде программного модуля:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйОборотыДтКт.СчетДт КАК СчетДт
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(,,, СчетДт.Код = ""20.01"") КАК ХозрасчетныйОборотыДтКт";
А теперь посмотрим на тот же запрос, подготовленный для работы непосредственно в Консоли запросов:
ВЫБРАТЬ
ХозрасчетныйОборотыДтКт.СчетДт КАК СчетДт
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(,,, СчетДт.Код = "20.01") КАК ХозрасчетныйОборотыДтКт
Проанализируем исходный запрос автора темы. В нем условие по подразделению наложено в секции ГДЕ:
ГДЕ ХозрасчетныйОборотыДтКт.ПодразделениеДт = &Подразделение
Почему это является грубым нарушением стандартов разработки 1С?
Когда мы накладываем условие в секции ГДЕ на виртуальную таблицу, СУБД сначала выбирает из базы данных абсолютно все обороты по указанному счету за весь выбранный период по всем подразделениям организации, строит временную таблицу в оперативной памяти сервера, и только после этого осуществляет фильтрацию по подразделению. На реальных базах данных с миллионами проводок такой запрос гарантированно приведет к долгому ожиданию, блокировкам таблиц и перегрузке процессора сервера СУБД.
Как сделать правильно?
Любые отборы по измерениям или субконто необходимо передавать строго в параметры самой виртуальной таблицы. В случае с таблицей ОборотыДтКт фильтр по дебету должен накладываться в четвертом параметре — УсловиеДт. Перепишем запрос правильно:
ВЫБРАТЬ
ХозрасчетныйОборотыДтКт.СчетДт,
ХозрасчетныйОборотыДтКт.ПодразделениеДт,
СУММА(ХозрасчетныйОборотыДтКт.СуммаОборот) КАК СуммаОборот
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(
&НачалоПериода,
&КонецПериода,
,
СчетДт.Код = "20.01" И ПодразделениеДт = &Подразделение
) КАК ХозрасчетныйОборотыДтКт
СГРУППИРОВАТЬ ПО
ХозрасчетныйОборотыДтКт.СчетДт,
ХозрасчетныйОборотыДтКт.ПодразделениеДт
В этом случае СУБД применит условия фильтрации на этапе первичного сканирования таблиц базы данных, используя соответствующие индексы, и выберет из базы только те записи, которые относятся к нужному подразделению.
В коде автора фигурирует следующая конструкция:
СУММА(ЕСТЬNULL(ХозрасчетныйОборотыДтКт.СуммаОборот, 0))
Рассмотрим подробнее, почему такое написание неоптимально с точки зрения СУБД. При вычислении функции СУММА(ЕСТЬNULL(Поле, 0)) сервер СУБД вынужден проверять на равенство NULL каждую детальную строку выборки до выполнения группировки. Если в выборке содержится 100 000 строк, функция ЕСТЬNULL выполнится ровно 100 000 раз.
Правильным подходом является вынесение проверки за скобки агрегатной функции:
ЕСТЬNULL(СУММА(ХозрасчетныйОборотыДтКт.СуммаОборот), 0)
При таком написании СУБД сначала быстро просуммирует значения (игнорируя значения NULL, что является стандартным поведением SQL), а проверка ЕСТЬNULL выполнится всего один раз — уже для результирующего сгруппированного значения.
Важный методологический нюанс: Для виртуальной таблицы ОборотыДтКт поле СуммаОборот физически не может содержать значение NULL в существующих записях регистра. Значение NULL может возникнуть только в результате левого соединения с другими таблицами или если запрос вернул пустой результат. Таким образом, применение функции ЕСТЬNULL в данном простом запросе является избыточным.
Использование конструкции СчетДт.Код = "20.01" крайне не рекомендуется разработчиками фирмы «1С». Проанализируем причины этого запрета:
СчетДт в регистре хранит ссылочный тип данных. Когда мы пишем СчетДт.Код, платформа 1С на уровне СУБД вынуждена транслировать запрос с добавлением левого соединения таблицы регистра бухгалтерии с таблицей плана счетов для получения текстового поля «Код». Это создает дополнительную нагрузку на дисковую подсистему и процессор сервера.Альтернативные варианты решения:
1. Использование конструкции ЗНАЧЕНИЕ() для обращения к предопределенному счету напрямую в базе данных (если счет является предопределенным):
СчетДт = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ОсновноеПроизводство)
2. Передача конкретного счета в качестве параметра запроса:
СчетДт = &ПараметрСчет
Оба метода позволяют СУБД сопоставлять данные напрямую по индексированному ссылочному полю, минуя лишние операции соединения таблиц.
В завершение разберем архитектурный выбор виртуальной таблицы. Автор темы использует РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт. Данная таблица является одной из самых «тяжелых» в системе, поскольку она сопоставляет дебетовые и кредитовые движения на уровне отдельных проводок.
Если перед нами стоит задача получить обороты дебета по счету 20.01 в разрезе подразделений без привязки к конкретным корреспондирующим счетам кредита, то использование ОборотыДтКт избыточно. Гораздо эффективнее использовать обычную таблицу Обороты:
ВЫБРАТЬ
ХозрасчетныйОбороты.Счет КАК Счет,
ХозрасчетныйОбороты.Субконто1 КАК Подразделение,
ХозрасчетныйОбороты.СуммаОборотДт КАК СуммаОборот
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(
&НачалоПериода,
&КонецПериода,
,
Счет = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ОсновноеПроизводство),
,
Субконто1 = &Подразделение
) КАК ХозрасчетныйОбороты
Такой запрос отработает в разы быстрее, так как таблица Обороты берет уже готовые рассчитанные итоги регистра бухгалтерии без необходимости выполнять сложное динамическое соединение дебетовых и кредитовых ветвей движений.