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