Курс валюты на дату документа (Запрос)

Часто на собеседованиях я задаю программистам вопрос: опишите алгоритм запроса получения курсов валют на даты документов, присутствующих в выборке для нижеуказанной ситуации.
Есть две валюты “Дерево”, “Камень”:

Есть регистр сведений “Курсы валют”:

И список документов на даты которых в одном запросе нужно получить курсы:

И тут у многих программистов возникают проблемы. Если бы регистр сведений “Курсы валют” имел бы значения на каждую дату, то почти все бы справились с помощью левого соединения. Но “пробелы” в регистре заставляют большую часть соискателей уйти в бесконечные раздумья составления алгоритма.
Что бы у вас не возникало в дальнейшем таких проблем рассмотрим алгоритм запроса по блокам:
- Делаем выборку по нашим документам:
1 2 3 4 5 6 7 8 9 10 11 |
// Выборка документов ВЫБРАТЬ ИсходныеДанные.Ссылка КАК Регистратор, ИсходныеДанные.Дата КАК Дата, ИсходныеДанные.Валюта КАК Валюта, ИсходныеДанные.Сумма КАК Сумма ПОМЕСТИТЬ ИсходныеДанные ИЗ Документ.ЗарплатаСотрудника КАК ИсходныеДанные ; |
- Из временной таблицы “Исходные данные” делаем выборку валют и дат, на которые нам нужно получить курсы:
1 2 3 4 5 6 7 8 9 10 11 |
// Выбираем для каких валют и на какие даты надо получить курсы //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ РАЗЛИЧНЫЕ ИсходныеДанные.Дата КАК Дата, ИсходныеДанные.Валюта КАК Валюта ПОМЕСТИТЬ ДатаВалюта ИЗ ИсходныеДанные КАК ИсходныеДанные ; |
- С помощью внутреннего соединения и группировки (по дате и валюте) получаем ближайшую дату курса из регистра сведений “Курсы валют”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// Выбираем ближайшую дату для курса валют //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ ДатаВалюта.Дата КАК Дата, ДатаВалюта.Валюта КАК Валюта, МАКСИМУМ(КурсыВалют.Период) КАК Период ПОМЕСТИТЬ ДатаКурса ИЗ ДатаВалюта КАК ДатаВалюта ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют ПО ДатаВалюта.Валюта = КурсыВалют.Валюта И ДатаВалюта.Дата >= КурсыВалют.Период СГРУППИРОВАТЬ ПО ДатаВалюта.Дата, ДатаВалюта.Валюта ; |
- Опять таки с помощью предыдущего типа соединения получаем нужные нам курсы валют на даты документов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// Получаем курсы валют на все даты, которые нужны //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ ДатаКурса.Дата КАК Дата, ДатаКурса.Валюта КАК Валюта, ДатаКурса.Период КАК Период, КурсыВалют.Курс КАК Курс, КурсыВалют.Кратность КАК Кратность ПОМЕСТИТЬ КурсыВалютСрез ИЗ ДатаКурса КАК ДатаКурса ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют ПО ДатаКурса.Валюта = КурсыВалют.Валюта И ДатаКурса.Период = КурсыВалют.Период ; |
- Объединяем первоначальные данные с временной таблицей “КурсыВалютСрез” и получаем то что нам и было нужно:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Объединяем исходные данные с курсами валют //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ ИсходныеДанные.Регистратор КАК Регистратор, ИсходныеДанные.Дата КАК Дата, ИсходныеДанные.Валюта КАК Валюта, ИсходныеДанные.Сумма КАК Сумма, КурсыВалютСрез.Период КАК Период, КурсыВалютСрез.Курс КАК Курс, КурсыВалютСрез.Кратность КАК Кратность, ИсходныеДанные.Сумма * КурсыВалютСрез.Курс / КурсыВалютСрез.Кратность КАК Итог ИЗ ИсходныеДанные КАК ИсходныеДанные ВНУТРЕННЕЕ СОЕДИНЕНИЕ КурсыВалютСрез КАК КурсыВалютСрез ПО ИсходныеДанные.Валюта = КурсыВалютСрез.Валюта И ИсходныеДанные.Дата = КурсыВалютСрез.Дата |
Результат будет полностью соответствовать ожиданиям:

Данный запрос можно было написать и с меньшим количеством временных таблиц, но для более четкого понимания я разбил его на такое количество блоков. Попробуйте оптимизировать и сделать его более компактным. Надеюсь теперь у вас с этим вопросом не возникнет трудностей.
Полный текст запроса для удобства:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
//Выборка документов ВЫБРАТЬ ИсходныеДанные.Ссылка КАК Регистратор, ИсходныеДанные.Дата КАК Дата, ИсходныеДанные.Валюта КАК Валюта, ИсходныеДанные.Сумма КАК Сумма ПОМЕСТИТЬ ИсходныеДанные ИЗ Документ.ЗарплатаСотрудника КАК ИсходныеДанные ; // Выбираем для каких валют на какие даты надо получить курсы валют //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ РАЗЛИЧНЫЕ ИсходныеДанные.Дата КАК Дата, ИсходныеДанные.Валюта КАК Валюта ПОМЕСТИТЬ ДатаВалюта ИЗ ИсходныеДанные КАК ИсходныеДанные ; // Выбираем ближайшую дату для курса валют //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ ДатаВалюта.Дата КАК Дата, ДатаВалюта.Валюта КАК Валюта, МАКСИМУМ(КурсыВалют.Период) КАК Период ПОМЕСТИТЬ ДатаКурса ИЗ ДатаВалюта КАК ДатаВалюта ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют ПО ДатаВалюта.Валюта = КурсыВалют.Валюта И ДатаВалюта.Дата >= КурсыВалют.Период СГРУППИРОВАТЬ ПО ДатаВалюта.Дата, ДатаВалюта.Валюта ; // Получаем курсы валют на все даты, которые нужны //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ ДатаКурса.Дата КАК Дата, ДатаКурса.Валюта КАК Валюта, ДатаКурса.Период КАК Период, КурсыВалют.Курс КАК Курс, КурсыВалют.Кратность КАК Кратность ПОМЕСТИТЬ КурсыВалютСрез ИЗ ДатаКурса КАК ДатаКурса ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют ПО ДатаКурса.Валюта = КурсыВалют.Валюта И ДатаКурса.Период = КурсыВалют.Период ; // Объединяем исходные данные с курсами валют //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ ИсходныеДанные.Регистратор КАК Регистратор, ИсходныеДанные.Дата КАК Дата, ИсходныеДанные.Валюта КАК Валюта, ИсходныеДанные.Сумма КАК Сумма, КурсыВалютСрез.Период КАК Период, КурсыВалютСрез.Курс КАК Курс, КурсыВалютСрез.Кратность КАК Кратность, ИсходныеДанные.Сумма * КурсыВалютСрез.Курс / КурсыВалютСрез.Кратность КАК Итог ИЗ ИсходныеДанные КАК ИсходныеДанные ВНУТРЕННЕЕ СОЕДИНЕНИЕ КурсыВалютСрез КАК КурсыВалютСрез ПО ИсходныеДанные.Валюта = КурсыВалютСрез.Валюта И ИсходныеДанные.Дата = КурсыВалютСрез.Дата |
Не бойтесь писать запросы любой сложности, это развивает мышление и повышает ваш уровень квалификации. Удачи!
интересненько.
Более оптимальный запрос:
https://craft1c.ru/1s-kurs-valjuty-na-datu-dokumenta-chast-2/