Курс валюты на дату каждого документа
Условие
Напишите запрос, который получает курс валюты на дату каждого документа.
Дано:
- Документ "РеализацияТоваровУслуг" с реквизитом "Валюта"
- Регистр сведений "КурсыВалют" (периодический) с измерением "Валюта" и ресурсом "Курс"
Требования
- Для каждого документа получить актуальный курс валюты на дату документа
- Использовать левое соединение (документы без курса тоже должны попасть в выборку)
Пример результата
| Документ | Дата | Валюта | Курс |
|---|---|---|---|
| Реал-001 | 01.01 | USD | 90.50 |
| Реал-002 | 02.01 | EUR | 98.30 |
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Получение курса валюты на дату документа
Это стандартная задача при работе с многовалютными документами. Нужно получить курс, который был актуален на дату документа.
Решение с использованием ПЕРИОДИЧНОСТЬ
Это основной подход в 1С для работы с периодическими регистрами сведений.
&НаСервере
Процедура ПолучитьКурсыДляДокументов()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РТУ.Ссылка КАК Документ,
| РТУ.Дата КАК Дата,
| РТУ.Валюта КАК Валюта,
| ЕСТЬNULL(КВ.Курс, 1) КАК Курс
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РТУ
|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КВ
| ПО РТУ.Валюта = КВ.Валюта
| И КВ.Период <= РТУ.Дата
|ГДЕ
| РТУ.Проведен = Истина
|ПОРЯДОК ПО
| РТУ.Дата";
Результат = Запрос.Выполнить().Выгрузить();
КонецПроцедуры
Проблема: неправильный результат
Вышеприведённый запрос имеет проблему — он вернёт несколько курсов для одного документа (все курсы, которые были <= даты документа).
Правильное решение с МАКСИМУМ
Для получения последнего актуального курса используем группировку:
&НаСервере
Процедура ПолучитьКурсыДляДокументов()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РТУ.Ссылка КАК Документ,
| РТУ.Дата КАК Дата,
| РТУ.Валюта КАК Валюта,
| ЕСТЬNULL(МАКСИМУМ(КВПодзапрос.Курс), 1) КАК Курс
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РТУ
|ЛЕВОЕ СОЕДИНЕНИЕ
| (ВЫБРАТЬ
| Валюта КАК Валюта,
| Период КАК Период,
| Курс КАК Курс
| ИЗ
| РегистрСведений.КурсыВалют) КАК КВПодзапрос
| ПО РТУ.Валюта = КВПодзапрос.Валюта
| И КВПодзапрос.Период <= РТУ.Дата
|ГДЕ
| РТУ.Проведен = Истина
|СГРУППИРОВАТЬ ПО
| РТУ.Ссылка,
| РТУ.Дата,
| РТУ.Валюта
|ПОРЯДОК ПО
| РТУ.Дата";
Результат = Запрос.Выполнить().Выгрузить();
ТаблицаДанных = Результат;
КонецПроцедуры
Проблема: медленный запрос при больших объёмах
Если кургов валют много и документов много, подзапрос будет медленным.
Оптимизированное решение
&НаСервере
Процедура ПолучитьКурсыДляДокументов()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РТУ.Ссылка КАК Документ,
| РТУ.Дата КАК Дата,
| РТУ.Валюта КАК Валюта,
| ЕСТЬNULL(МАКСИМУМ(КВ.Период) КАК МаксПериод
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РТУ
|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КВ
| ПО РТУ.Валюта = КВ.Валюта
| И КВ.Период <= РТУ.Дата
|ГДЕ
| РТУ.Проведен = Истина
|СГРУППИРОВАТЬ ПО
| РТУ.Ссылка,
| РТУ.Дата,
| РТУ.Валюта";
РезультатПериодов = Запрос.Выполнить().Выгрузить();
// Второй запрос для получения курсов
Запрос2 = Новый Запрос;
Запрос2.Текст =
"ВЫБРАТЬ
| РП.Документ,
| РП.Дата,
| РП.Валюта,
| ЕСТЬNULL(КВ.Курс, 1) КАК Курс
|ИЗ
| &ДанныеПериодов КАК РП
|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КВ
| ПО РП.Валюта = КВ.Валюта
| И КВ.Период = РП.МаксПериод";
Запрос2.УстановитьПараметр("ДанныеПериодов", РезультатПериодов);
Результат = Запрос2.Выполнить().Выгрузить();
ТаблицаДанных = Результат;
КонецПроцедуры
Решение с использованием цикла (для небольших объёмов)
&НаСервере
Процедура ПолучитьКурсыДляДокументов()
// Сначала получаем документы
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РТУ.Ссылка КАК Документ,
| РТУ.Дата КАК Дата,
| РТУ.Валюта КАК Валюта
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РТУ
|ГДЕ
| РТУ.Проведен = Истина";
Выборка = Запрос.Выполнить().Выбрать();
ТаблицаРезультатов = Новая ТаблицаЗначений();
ТаблицаРезультатов.Колонки.Добавить("Документ");
ТаблицаРезультатов.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
ТаблицаРезультатов.Колонки.Добавить("Валюта");
ТаблицаРезультатов.Колонки.Добавить("Курс", Новый ОписаниеТипов("Число"));
Пока Выборка.Следующий() Цикл
НоваяСтрока = ТаблицаРезультатов.Добавить();
НоваяСтрока.Документ = Выборка.Документ;
НоваяСтрока.Дата = Выборка.Дата;
НоваяСтрока.Валюта = Выборка.Валюта;
// Ищем курс на дату документа
НоваяСтрока.Курс = ПолучитьКурсНаДату(Выборка.Валюта, Выборка.Дата);
КонецЦикла;
// Теперь ТаблицаРезультатов содержит нужные данные
КонецПроцедуры
&НаСервере
Функция ПолучитьКурсНаДату(Валюта, Дата)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПЕРВЫЕ 1
| КВ.Курс
|ИЗ
| РегистрСведений.КурсыВалют КАК КВ
|ГДЕ
| КВ.Валюта = &Валюта
| И КВ.Период <= &Дата
|ПОРЯДОК ПО
| КВ.Период УБЫВ";
Запрос.УстановитьПараметр("Валюта", Валюта);
Запрос.УстановитьПараметр("Дата", Дата);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Курс;
Иначе
Возврат 1; // Курс по умолчанию
КонецЕсли;
КонецФункции
Альтернатива: использование ЕСТЬNULL с МАКСИМУМ в подзапросе
Запрос.Текст =
"ВЫБРАТЬ
| РТУ.Ссылка КАК Документ,
| РТУ.Дата КАК Дата,
| РТУ.Валюта КАК Валюта,
| ЕСТЬNULL(КВПоследний.Курс, 1) КАК Курс
|ИЗ
| Документ.РеализацияТоваровУслуг КАК РТУ
|ЛЕВОЕ СОЕДИНЕНИЕ
| (ВЫБРАТЬ
| Валюта,
| МАКСИМУМ(Период) КАК МаксПериод
| ИЗ
| РегистрСведений.КурсыВалют
| СГРУППИРОВАТЬ ПО
| Валюта) КАК КВМакс
| ПО РТУ.Валюта = КВМакс.Валюта
|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КВПоследний
| ПО КВМакс.Валюта = КВПоследний.Валюта
| И КВМакс.МаксПериод = КВПоследний.Период
| И КВПоследний.Период <= РТУ.Дата
|ГДЕ
| РТУ.Проведен = Истина";
Ключевые моменты
1. ЛЕВОЕ СОЕДИНЕНИЕ — документы без курса попадут в выборку с ЕСТЬNULL
2. Условие Период <= Дата — получаем курс на дату документа или раньше
3. МАКСИМУМ(Период) — берём последний актуальный курс
4. Группировка — без группировки запрос вернёт дублей
5. ЕСТЬNULL(курс, 1) — если курса нет, используем курс 1
Рекомендация
Для большинства случаев используй второе решение с МАКСИМУМ — оно правильное и быстрое. Если остаётся медленным при большом объёме — переходи на трёхэтапное решение с двумя запросами.