Как известно в 1С 8 виртуальная таблица "срез последних" периодического регистра сведений раскрывается в подзапрос, составленный из таблицы регистра, соединенной сама с собой. То есть вот это
ВЫБРАТЬ
ЦеныНоменклатурыСрезПоследних.Номенклатура,
ЦеныНоменклатурыСрезПоследних.Валюта,
ЦеныНоменклатурыСрезПоследних.Цена,
ЦеныНоменклатурыСрезПоследних.ЕдиницаИзмерения
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ТипЦен = &ТипЦен) КАК ЦеныНоменклатурыСрезПоследних
превращает платформа 1С примерно в такой запрос:
ВЫБРАТЬ
ЦеныНоменклатуры.Номенклатура,
ЦеныНоменклатуры.Валюта,
ЦеныНоменклатуры.Цена,
ЦеныНоменклатуры.ЕдиницаИзмерения
ИЗ
(ВЫБРАТЬ
ЦеныНоменклатуры.Номенклатура КАК Номенклатура,
ЦеныНоменклатуры.ТипЦен КАК ТипЦен,
МАКСИМУМ(ЦеныНоменклатуры.Период) КАК Период
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ГДЕ
ЦеныНоменклатуры.Период <= &Дата
И ЦеныНоменклатуры.ТипЦен = &ТипЦен
СГРУППИРОВАТЬ ПО
ЦеныНоменклатуры.Номенклатура,
ЦеныНоменклатуры.ТипЦен) КАК ВложенныйЗапрос
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ПО ВложенныйЗапрос.Номенклатура = ЦеныНоменклатуры.Номенклатура
И ВложенныйЗапрос.ТипЦен = ЦеныНоменклатуры.ТипЦен
И ВложенныйЗапрос.Период = ЦеныНоменклатуры.Период
Аналогичные вещи приходится писать и в случае "среза последних на каждую дату в запросе": Классический вариант, Альтернативный и их сравнение.
Гораздо удобнее были бы запросы такого вида:
ВЫБРАТЬ
ЦеныНоменклатуры.Номенклатура,
ЦеныНоменклатуры.Валюта,
ЦеныНоменклатуры.Цена,
ЦеныНоменклатуры.ЕдиницаИзмерения
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ГДЕ
ЦеныНоменклатуры.ТипЦен = &ТипЦен
И &Дата МЕЖДУ ЦеныНоменклатуры.Период И ЦеныНоменклатуры.ПериодОкончания
Но что это за поле ПериодОкончания и как оно должно быть заполнено?
Это был добавлен реквизит регистра сведений с типом "ДатаВремя". Ни в коем случае это поле не должно быть измерением, но вполне могло быть ресурсом, ведь в запросах ресурсы и реквизиты регистра сведений ничем не различимы. Реквизитом, а не ресурсом оно сделано чисто из эстетических принципов )). Зато встроенный механизм виртуальной таблицы "срез последних" нисколько не поврежден и может по-прежнему использоваться.
Заполняется "период окончания" согласно правилу: Если есть запись с тем же набором измерений, стоящая следующей в хронологии, то берется её период за вычетом одной секунды, в противном случае решено было взять дату 01.01.3000. Пример значений периодов таблицы по одному набору измерений:
Период | ПериодОкончания |
13.07.09 0:00:00 | 07.09.09 23:59:59 |
08.09.09 0:00:00 | 13.09.09 23:59:59 |
14.09.09 0:00:00 | 13.12.09 23:59:59 |
14.12.09 0:00:00 | 15.03.10 23:59:59 |
16.03.10 0:00:00 | 18.07.10 23:59:59 |
19.07.10 0:00:00 | 09.09.10 23:59:59 |
10.09.10 0:00:00 | 15.11.10 23:59:59 |
16.11.10 0:00:00 | 17.04.11 23:59:59 |
18.04.11 0:00:00 | 10.07.11 23:59:59 |
11.07.11 0:00:00 | 03.10.11 23:59:59 |
04.10.11 0:00:00 | 19.01.12 23:59:59 |
20.01.12 0:00:00 | 21.10.12 23:59:59 |
22.10.12 0:00:00 | 01.01.3000 0:00:00 |
Естественно это поле должно заполняться автоматически. А значит нужно использовать события модуля набора записей ПередЗаписью и ПриЗаписи. Но так как база клиент-серверная в привязке с MS SQL Server, то воспользовались триггерами СУБД After Insert/Update/Delete. Впрочем триггер AfterUpdate был для перестраховки - 1С не изменяет записи регистра сведений, она их удаляет и вставляет заново. Вот пример триггера After Insert.
Индексирование реквизита средствами 1С привело к созданию составного индекса: Период+Все измерения. Помимо этого был добавлен индекс средствами SQL: Период+ПериодОкончания+Период окончания.
Да, все эти манипуляции приводят к уменьшению скорости записи, но когда запись происходит гораздо реже чтения, то затраты оправданы.
Аналогичная операция была сделана с курсами валют. Проведены тесты (1000 выполнений запроса) с разными параметрами на запросах:
1. Стандартные срезы:
ВЫБРАТЬ
ЦеныНоменклатурыСрезПоследних.Номенклатура,
ЦеныНоменклатурыСрезПоследних.Цена * КурсыВалютСрезПоследних.Курс / ЦеныНоменклатурыСрезПоследних.ЕдиницаИзмерения.Коэффициент КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата,
ТипЦен = &ТипЦен
И Номенклатура В ИЕРАРХИИ (&Номенклатура)) КАК ЦеныНоменклатурыСрезПоследних
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&Дата, ) КАК КурсыВалютСрезПоследних
ПО ЦеныНоменклатурыСрезПоследних.Валюта = КурсыВалютСрезПоследних.Валюта
ВЫБРАТЬ
ЦеныНоменклатуры.Номенклатура,
ЦеныНоменклатуры.Цена * КурсыВалют.Курс / ЦеныНоменклатуры.ЕдиницаИзмерения.Коэффициент КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
ПО ЦеныНоменклатуры.Валюта = КурсыВалют.Валюта
И (&Дата МЕЖДУ ЦеныНоменклатуры.Период И КурсыВалют.ПериодОкончания)
ГДЕ
&Дата МЕЖДУ ЦеныНоменклатуры.Период И ЦеныНоменклатуры.ПериодОкончания
И ЦеныНоменклатуры.ТипЦен = &ТипЦен
И ЦеныНоменклатуры.Номенклатура В ИЕРАРХИИ(&Номенклатура)
Срез последних | Модификация | Экономия времени, % |
0,235 | 0,191 | 18,72 |
0,232 | 0,199 | 14,22 |
0,228 | 0,206 | 9,65 |
0,210 | 0,189 | 10,00 |
Замечание 1: срез последних без указания параметра даты, это срез на самую последнюю дату в таблице. У нас это запрос:
ВЫБРАТЬ
ЦеныНоменклатуры.Номенклатура,
ЦеныНоменклатуры.Валюта,
ЦеныНоменклатуры.Цена,
ЦеныНоменклатуры.ЕдиницаИзмерения
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ГДЕ
ЦеныНоменклатуры.ТипЦен = &ТипЦен
И ЦеныНоменклатуры.ПериодОкончания=ДАТАВРЕМЯ(3000,1,1)
ЦеныНоменклатуры.Номенклатура,
ЦеныНоменклатуры.Валюта,
ЦеныНоменклатуры.Цена,
ЦеныНоменклатуры.ЕдиницаИзмерения
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ГДЕ
ЦеныНоменклатуры.ТипЦен = &ТипЦен
И ЦеныНоменклатуры.ПериодОкончания=ДАТАВРЕМЯ(3000,1,1)
Замечание 2: все записи считаются активными..
Замечание 3: здесь не рассматривается случай периодичности "позиция регистратора".