В практике разработки на платформе 1С часто возникает задача получения актуального курса валюты на дату конкретного документа. Если таких документов много (например, отчет за год), стандартные методы могут работать медленно. В этой статье мы разберем несколько подходов к оптимизации таких запросов: от классического использования временных таблиц до продвинутых методов исключения лишних соединений.
Рассмотрим типовую ситуацию: нам нужно выбрать список документов РеализацияТоваровУслуг за период и для каждого определить курс валюты, действовавший на момент совершения операции. Основная сложность заключается в том, что в регистре сведений КурсыВалют записи существуют не на каждый день, а только на даты изменения курса. Нам необходимо найти «срез последних» на каждую дату документа.
Типичная ошибка начинающего разработчика — выполнение запроса в цикле или использование соединения, которое порождает избыточное сканирование таблиц. Разберем, как сделать это правильно.
Один из самых эффективных способов ускорить расчет — сократить количество записей, для которых ищется курс. Часто в выборке документов за день много, а валют всего несколько. Проанализируем, как можно оптимизировать этот процесс.
Шаг 1. Подготовка уникальных данных. Вместо того чтобы искать курс для каждой ссылки на документ, мы сначала выберем уникальные пары «Дата» и «Валюта» во временную таблицу. Это критически важно для производительности.
Шаг 2. Индексация. Обязательно добавим индексирование временной таблицы, чтобы последующие соединения происходили мгновенно.
Рассмотрим пример кода:
ВЫБРАТЬ РАЗЛИЧНЫЕ
НАЧАЛОПЕРИОДА(РеализацияТоваровУслуг.Дата, ДЕНЬ) КАК ДатаДокумента,
РеализацияТоваровУслуг.ВалютаДокумента КАК Валюта
ПОМЕСТИТЬ УникальныеДатыВалют
ИЗ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
РеализацияТоваровУслуг.Дата МЕЖДУ &ДатаНач И &ДатаКон
ИНДЕКСИРОВАТЬ ПО
ДатаДокумента,
Валюта
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
УникальныеДатыВалют.ДатаДокумента КАК ДатаДокумента,
УникальныеДатыВалют.Валюта КАК Валюта,
МАКСИМУМ(КурсыВалют.Период) КАК ПериодКурса
ПОМЕСТИТЬ МаксимальныеДатыКурсов
ИЗ
УникальныеДатыВалют КАК УникальныеДатыВалют
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
ПО УникальныеДатыВалют.Валюта = КурсыВалют.Валюта
И УникальныеДатыВалют.ДатаДокумента >= КурсыВалют.Период
СГРУППИРОВАТЬ ПО
УникальныеДатыВалют.ДатаДокумента,
УникальныеДатыВалют.Валюта
ИНДЕКСИРОВАТЬ ПО
Валюта,
ПериодКурса
;
В этом подходе мы минимизируем нагрузку на СУБД, так как поиск максимальной даты курса выполняется только для уникальных комбинаций, а не для каждой реализации.
Иногда требуется получить данные одним запросом без использования временных таблиц (например, в динамических списках или простых отчетах). В этом случае можно применить логику «будущего периода», которая отсекает все записи, кроме самой последней актуальной.
Проанализируем пример такого запроса:
ВЫБРАТЬ
ТТ.Ссылка КАК Документ,
Курсы.Курс КАК Курс,
Курсы.Кратность КАК Кратность
ИЗ
Документ.РеализацияТоваровУслуг КАК ТТ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Курсы
ПО Курсы.Валюта = ТТ.ВалютаДокумента
И Курсы.Период <= ТТ.Дата
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Будущее
ПО Будущее.Валюта = ТТ.ВалютаДокумента
И Будущее.Период > Курсы.Период
И Будущее.Период <= ТТ.Дата
ГДЕ
Будущее.Период ЕСТЬ NULL
И ТТ.Дата МЕЖДУ &ДатаНач И &ДатаКон
Разберем, как это работает:
Будущее) пытается найти курс, который был установлен позже найденного курса, но всё еще до даты документа.Будущее.Период ЕСТЬ NULL оставляет только ту запись из первого соединения, для которой не нашлось более «свежей» записи. Это и будет курс на дату документа.Важно: Этот метод элегантен, но может работать медленнее, чем пакетный запрос с временными таблицами, если в регистре очень много записей на одну дату или сложные условия индексации в СУБД.
Если ваша система высоконагружена и запросы курсов валют выполняются постоянно в больших объемах, лучшим решением будет денормализация. Выясним, почему это работает лучше всего.
Многие типовые конфигурации (например, УТ 11, ERP 2) хранят курс и кратность непосредственно в реквизитах документа. Это позволяет:
Если в вашей конфигурации этого нет, можно рассмотреть создание вспомогательного регистра сведений, где регламентным заданием будут заполняться курсы на каждый календарный день. Тогда запрос превратится в простое соединение по равенству дат Документ.Дата = Регистр.Период, что является самой быстрой операцией для СУБД.
При оптимизации запросов по валютам часто упускают из виду важные технические детали:
1. Кратность. Никогда не выбирайте только Курс. Для многих валют (иена, тенге) курс устанавливается за 10, 100 или 1000 единиц. Всегда учитывайте поле Кратность в расчетах.
2. Обработка NULL. Если на дату документа курс еще не был введен в систему, левое соединение вернет NULL. Рекомендуется использовать функцию ЕСТЬNULL(Курс, 1), чтобы избежать ошибок деления на ноль или пустых значений в отчетах.
3. Ограничение выборки регистра. Чтобы ускорить поиск максимальной даты, можно добавить условие на период самого регистра КурсыВалют. Если ваши документы за 2023 год, нет смысла позволять СУБД просматривать курсы за 2010 год. Добавьте фильтр КурсыВалют.Период > &НачалоЭпохи, где &НачалоЭпохи — дата первого документа в системе или начало текущего года.
Подводя итог, скажем: для большинства задач в 1С оптимальным является использование пакетного запроса с индексированными временными таблицами уникальных пар «Дата + Валюта». Это обеспечивает баланс между скоростью разработки и производительностью системы.