среда, 11 сентября 2013 г.

Альтернатива периодическому регистру сведений в клиент-серверной базе данных 1С 8

Как известно в 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. Стандартные срезы:

ВЫБРАТЬ
ЦеныНоменклатурыСрезПоследних.Номенклатура,
ЦеныНоменклатурыСрезПоследних.Цена * КурсыВалютСрезПоследних.Курс / ЦеныНоменклатурыСрезПоследних.ЕдиницаИзмерения.Коэффициент КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата,
ТипЦен = &ТипЦен
И Номенклатура В ИЕРАРХИИ (&Номенклатура)) КАК ЦеныНоменклатурыСрезПоследних
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют.СрезПоследних(&Дата, ) КАК КурсыВалютСрезПоследних
ПО ЦеныНоменклатурыСрезПоследних.Валюта = КурсыВалютСрезПоследних.Валюта

2. Модифицированные регистры:

ВЫБРАТЬ
ЦеныНоменклатуры.Номенклатура,
ЦеныНоменклатуры.Цена * КурсыВалют.Курс / ЦеныНоменклатуры.ЕдиницаИзмерения.Коэффициент КАК Цена
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
ПО ЦеныНоменклатуры.Валюта = КурсыВалют.Валюта
И (&Дата МЕЖДУ ЦеныНоменклатуры.Период И КурсыВалют.ПериодОкончания)
ГДЕ
&Дата МЕЖДУ ЦеныНоменклатуры.Период И ЦеныНоменклатуры.ПериодОкончания
И ЦеныНоменклатуры.ТипЦен = &ТипЦен
И ЦеныНоменклатуры.Номенклатура В ИЕРАРХИИ(&Номенклатура)

Срез последних Модификация   Экономия времени,   %
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)

Замечание 2: все записи считаются активными..
Замечание 3: здесь не рассматривается случай периодичности "позиция регистратора".

Комментариев нет:

Отправить комментарий