В практике разработчика 1С иногда встречается коварная проблема: один и тот же отчет при запуске в рабочей базе выдает разные результаты, хотя данные за период, казалось бы, никто не менял. Ситуация усугубляется, когда в копии базы все работает стабильно. Проанализируем, почему так происходит, и разберем по шагам, как привести такой запрос в чувство, сделав его быстрым и надежным.
Первым делом разберем ситуацию, когда запрос выполняется крайне долго (например, около часа). В этом случае в рабочей базе на результат начинают влиять внешние факторы, которые отсутствуют в копии. Рассмотрим основные причины:
Read Committed. Пока ваш запрос целый час «гуляет» по таблицам, другие пользователи проводят документы. Несмотря на заверения методистов, система может делать автоматические корректировки или закрытие месяца, что меняет итоги в процессе выполнения вашего запроса.ОборотыДтКт — одна из самых ресурсоемких. Она не существует физически, а собирается СУБД в момент обращения. Если в процессе этого «сбора» происходит обновление итогов или запись в регистр, результат может варьироваться.Важный вывод: Любой запрос, работающий более нескольких минут в транзакционной базе, потенциально нестабилен. Единственный верный способ борьбы с этим — кардинальное ускорение кода.
Рассмотрим пример кода, который часто становится причиной тормозов. Основная ошибка здесь — многократное обращение к тяжелой виртуальной таблице с использованием сложных фильтров внутри параметров. Проанализируем ситуацию на примере:
// Неоптимальный подход:
// Пять обращений к одной и той же виртуальной таблице с разными субконто
ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Данные1 ИЗ РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(..., &ВидыСубконто1, ...);
ВЫБРАТЬ ... ПОМЕСТИТЬ ВТ_Данные2 ИЗ РегистрБухгалтерии.Хозрасчетный.ОборотыДтКт(..., &ВидыСубконто2, ...);
Разберем, как сделать этот процесс эффективнее. Вместо того чтобы заставлять сервер пять раз пересчитывать обороты, мы должны стремиться к получению данных «одним куском».
Использование конструкции В ИЕРАРХИИ внутри параметров виртуальной таблицы, особенно если там используется подзапрос, — это «смерть» производительности. SQL-серверу крайне тяжело оптимизировать такой план запроса.
Решение: Подготовим плоский список всех нужных счетов (включая все субсчета) заранее во временную таблицу и обязательно её проиндексируем.
ВЫБРАТЬ
Хозрасчетный.Ссылка КАК Счет
ПОМЕСТИТЬ ВТ_ВсеСчета
ИЗ
ПланСчетов.Хозрасчетный КАК Хозрасчетный
ГДЕ
Хозрасчетный.Ссылка В ИЕРАРХИИ (&СписокКорневыхСчетов)
ИНДЕКСИРОВАТЬ ПО
Счет
;
Теперь в основном запросе вместо В ИЕРАРХИИ мы будем использовать простое условие В (ВЫБРАТЬ Счет ИЗ ВТ_ВсеСчета). Это позволит оптимизатору SQL применить эффективное соединение таблиц.
Проанализируем работу с временными таблицами (ВТ). Часто программисты забывают, что ВТ без индексов на больших объемах данных работают медленно. Если вы создали ВТ НашиСчета и используете её в нескольких соединениях, добавьте ИНДЕКСИРОВАТЬ ПО.
Также важно сократить количество обращений к виртуальным таблицам регистра бухгалтерии. Рассмотрим стратегию:
1. Получаем максимально широкий набор данных в одну временную таблицу ВТ_ОбщиеОбороты без указания конкретных видов субконто (если это возможно) или с максимальным их набором.
2. Далее работаем уже с этой временной таблицей, фильтруя её и группируя по нужным разрезам.
Если виртуальная таблица ОборотыДтКт продолжает работать медленно, вспомним старую мудрость: виртуальные таблицы удобны, но не всегда быстры. В сложных случаях, когда нужно выжать максимум скорости, проанализируем возможность обращения к физической таблице движений РегистрБухгалтерии.Хозрасчетный.
При использовании физической таблицы мы сами управляем соединениями с таблицами субконто. Это избавляет от лишних LEFT JOIN, которые платформа 1С щедро добавляет при использовании ВЫРАЗИТЬ(Субконто КАК ...). Однако помните, что такой запрос будет гораздо сложнее в написании, так как вам придется самостоятельно обрабатывать порядок субконто и знаки оборотов.
При получении субконто из виртуальных таблиц они имеют составной тип. Посмотрим, как это влияет на запрос. Каждое ВЫРАЗИТЬ для поля Субконто заставляет SQL-сервер присоединять таблицу соответствующего справочника. Если у вас 3 субконто и вы используете ВЫРАЗИТЬ для каждого, это +3 соединения к каждой выборке.
Проанализируем ситуацию: если вам не нужны поля из самих справочников (наименование, реквизиты) прямо в этом запросе, возможно, стоит получить просто ссылки, а типизировать их уже при выводе или в самом конце пакета запросов.
Поскольку отчет выполняется в фоновом задании, выясним причины возможных отличий в поведении от обычной формы:
Итоговые рекомендации для стабильной работы:
Подразделение) встречаются NULL или битые значения, которые по-разному обрабатываются при построении виртуальных таблиц. Проведите тестирование и исправление базы.Read Committed Snapshot Isolation поможет отчету видеть согласованный «снимок» данных на момент начала запроса, не блокируясь при этом пишущими пользователями.Следуя этим шагам, мы не только избавимся от «магии» с меняющимися цифрами, но и значительно повысим комфорт работы пользователей, превратив тяжелый часовой отчет в быстрый и точный инструмент аналитики.