SQL версия. Компоненты ОУ и БУ.
Довольно часто при решении задач в системе 1С:Предприятие возникает необходимость в групповом проведении документов. Одна из основных причин - исправление ошибок, которые могут возникнуть за какой-либо прошлый период. Иногда это является частью алгоритма работы. Например, в течение одного месяца ведется только количественный оперативный учет, а затем, в конце месяца, формируются бухгалтерские проводки и суммовой учет в оперативной подсистеме. Есть еще много причин, но статья посвящена не этому. Цель статьи разобраться как можно ускорить данную обработку в системе 1С:Предприятие 7.7 SQL версии. Довольно часто приходится слышать, что в этой версии групповое проведение работает значительно медленнее, чем в DBF версии.
Сразу хочу заметить, что групповое проведение в 1С можно реализовать несколькими способами: своей обработкой, через меню 'Операции - Проведение документов :'. При этом второй способ как вы наверное могли заметить работает быстрее чем проведение документов с помощью обработки (по сути проведение документов с помощью метода Провести). Дело в том, как мне кажется, в этом случае система оптимизирует расчет остатков при проведении документов. Рассматривая через Profiler что же творится в этот момент, я сделал вывод, что это именно так. По крайней мере, я не обнаружил сложных вычислений остатков, а также обнаружил некоторые 'подозрительные' манипуляции с таблицей остатков. Долго разбираться не хватило сил - при проведении всего двух документов список сообщений в Profiler состоялљ из нескольких десятков страниц.
Хотелось бы заметить также, что и в первом случае возможно оптимизированное проведение документов. Для этого можно использовать метод Актуальность. Прочтя документацию 1С и дополнительные материалы, я к сожалению не нашел нигде упоминания об использовании этого метода для оптимизации проведения документов оперативного учета. То есть применять его (следуя документации) можно только для проведения документов использующих бухгалтерскую компоненту. Пример использования данного метода можно посмотреть в стандартной обработке бухгалтерских конфигураций 1С "ОбработкаДокументов".
На этом закончим рассмотрение стандартных способов оптимизации. Перейдем к основной части статьи - оптимизация с использованием собственных алгоритмов. Почему слово 'ускоряемся' стоит под знаком вопроса, спросите вы. Да потому что это всего лишь теоретические выкладки, которые нужно проверять на практике и самое главное - произвести сравнение со стандартными методами. Если вдруг случится такое невероятное событие, что у меня появится неделька свободного времени, то вполне возможно, что я проведу такие испытания и при их успешном завершении (то есть если будет получен значительный выигрыш) включу данную возможность в свою компоненту ToySQL.
Итак (наконец-то ;) сам алгоритм.
Идея алгоритма заключается в следующем. При расчете итогов мы сохраняем их (то есть рассчитанные итоги) во временной таблице. При этом нужно выбрать периодичность сохранения остатков. Я предлагаю делать это с периодичностью в день. Можно конечно хранить остатки на каждый рассчитанные документ, но при этом обЪем данных будет слишком велик (особенно если документов много). Хотя можно внести изменения в алгоритм так, чтобы лишние данные удалялись - проще всего это будет делать если проведение документов идет в одном направлении (по времени). Обычно это так, но пока оставим рассмотрение этого момента на потом.
Остатки будем хранить только по тем позициям, для которых они рассчитываются в текущем документе. Этот момент очень важен. Например, если документы проводятся только по определенным позициям или если случилось так, что в этом периоде по документам 'прошло' только какое-то подмножество измерения регистра, то расчет остатков существенно ускорится. Таким образом, достигается высокая гибкость алгоритма. Также важный момент алгоритма в том, что не обязательно проведение документов производить в одном направлении, так как остатки хранятся и за предыдущие дни.
Рассмотрим работу алгоритма на одном регистре.
Входные данные:
Регистр хранения остатков <ТабОст, ТабДвиж>,
ТабОст = ФО(Изм1, Изм2, .. ИзмН, Период) -> Рес,
ТабДвиж = ФД(Изм1, Изм2, .. ИзмН, Документ) -> Рес,
Последовательность проведения документов
ДП = {ДI = <Дата, Изм1, Изм2, .. ИзмН>}I = 1..N.
Вспомогательные данные:
Промежуточная таблица хранения итогов:
ВремИтоги = ФВ( Область)->Рес. Область ={ <Изм1, Изм2, .. ИзмН, Период>}. Таблица упорядочена по тем же измерениям и в том же порядке что и в регистре. Можно в индекс добавить также Период.
Функция расчета остатка на любую дату (документ):
Ф(Изм1, Изм2, .. ИзмН, Дата_Документ) =
?(Дата_Документ = КонМесяца(Дата_Документ),
ФО(Изм1, Изм2, .. ИзмН, Дата_Документ),
ФО(Изм1, Изм2, .. ИзмН, НачМесяца(Дата_Документ)-1) +
å ФД(Изм1, Изм2, .. ИзмН, Документ Î {Д|(Д.Дата >= НачМесяца(Дата_Документ)) и
(Д.Дата <= Дата_Документ)}))
Функция получения подмножества измерений:
Изм({Изм1, Изм2, .. ИзмН, Период}) = {<Изм1, Изм2, .. ИзмН>}
Выходные данные:
Оптимизированное значение функции Ф = ФОПТ для последовательности документов ДП.
Алгоритм
Для И = 1 по Н Цикл НадоСчитать = Изм(ДИ - ВремИтоги.Область * ДИ) // Вычислить новое значение функции ФВ Втаблице = { <Y,Z> Î ВремИтоги.Область | $Y Î НадоСчитать "<Y,L> Î ВремИтоги.Область (L <= Z) } ФВ(Х = <Y,Дата> Î Втаблице) = ФВ(Х) - ФД(Y;Документ Î {Д|(Д > Дата) и (Д.Дата <= ДИ.Дата)} ФВ(Х = <Y,ДИ.Дата>,Y Î (НадоСчитать - Изм(Втаблице))) = Ф(Y; ДИ.Дата) // Все остатки для текущего документа ФОПТ(Х = <Y,Дата> Î ДИ) = ФВ(Х) - ФД(Y;Документ Î {Д|(Д >= ДИ) и (Д.Дата <= ДИ.Дата)} КонецЦикла
Прошу прощения если допустил какие-то грубые или не очень ошибки в описании алгоритма таким образом. Очень хотелось записать алгоритм как можно короче - использование математического языка в этом случае очень помогает. Самое главное кто с ним знаком, то все понятно. Не ручаюсь за правильность использования данного языка - давно не было практики, а в учебники как-то лень заглядывать (для начала их еще найти надо ;).
Пояснения для неграмотных :):
Таблица остатков и движений суть функции по измерениям и периоду (дате). Значение функции в общем случае числовой вектор. В моем случае ресурс один. Документ рассматриваем как множество кортежей, то есть реляционная таблица. Считаем, что все значения реквизитов и значение даты документа хранятся в одной таблице. Промежуточная таблица итогов похожа на таблицу остатков с той лишь разницей, что периодичность их хранения один день. Функция расчета остатка - понятно без обЪяснений (так считает остатки сама 1С). На выходе мы должны получить функцию аналогичной Ф, результат вычисления которой для последовательности документов ДП будет более оптимальным с точки зрения производительности.
В процессе формулировки последнего предложения меня посетила мысль, что результатом данного алгоритма должна стать не более оптимальная функция, а просто функция. Не знаю есть ли такое понятие в математическом аппарате как оптимальность функции по отношению к другой, но то что существует оценка алгоритма, реализующего функцию это точно. К сожалению я не владею данным аппаратом, но думаю, что тем, кто с этим знаком не составит труда произвести оценку первого алгоритма реализующего функцию Ф (то есть простой ее вариант, рассмотренный в секции вспомогательные данные) и алгоритма реализующего эту же функцию, но названную как ФОПТ. То есть функция одна, а алгоритма два.
Пояснения к самому алгоритму. Для каждого документа мы находим множество измерений (НадоСчитать), которые отсутствуют во временной таблице. Затем в таблице итогов выбираем остатки, которые максимально близки к необходимым для расчета, содержащимся в множестве НадоСчитать. Добавляем во временные итоги вновь рассчитанные остатки. Первая часть по множеству измерений, которые 'отталкиваются' от таблицы временных итогов, вторая часть рассчитывается с помощью 'неоптимизированной функции'. В конце считаем остатки на документ - из остатка на дату документам, которые берутся из временных итогов, вычитаем лишние движения.
Вот и все. Остается произвести оценку алгоритма, как было описано выше, и затем подтвердить ее на практике.
Приложение. Реализация на T-SQL
Здесь я рассмотрю некоторые шаги алгоритма, реализованные на T-SQL. Структура таблиц (похожа на 1С, но не совсем):
Регистр
Итоги (rg)
izm0izm 1
Period
Res
Движения (ra)
Izm0
Izm1
Datedoc
Time
Debkred
Res
Остальные таблицы имеют структуру аналогичную итогам регистра. @DateDoc - дата текущего документа, @Timedoc - время текущего документа, @BegMonth - начало месяца для @DateDoc.
Определение множества НадоСчитать:
Select d.izm0, d. izm1 into #need_calc from #temp_tot t, #doc_data d
Where t. izm 0 = d. izm0 and t. izm1 = d. izm1 and t.period <> d.period
Определение множества Втаблице:
Select t.* into #in_temp from #need_calc n, #temp_tot t
Where n.izm0 = t.izm0 and n.izm1 = t. izm1 and
t.period = (select max(tt.period) from #temp_tot tt
where tt.izm0 = t.izm0 and tt.izm1 = t.izm1 and tt.period <= t.period)
Расчет остатков по множеству Втаблице:
Insert #temp_tot
Select i.izm0,i.izm1,sum(t.res+(1-2*ra.debkred)*ra.res)
from #in_temp I
left join #temp_tot t on t.izm0 = i.izm0 and t.izm1 = i.izm1 and t.period =
i.period
left join rg on rg.izm0 = i.izm0 and rg.izm1 = i.izm1 and i.period < rg.Datedoc
and rg.Datedoc <= @DateDoc
Расчет остатков, которых нет в таблице временных итогов:
Insert #temp_tot
Select i.izm0,i.izm1,sum(rg.res+(1-2*ra.debkred)*ra.res)
from #need_calc n
left join rg on n.izm0 = rg.izm0 and n.izm1 = rg.izm1 and rg.period = dateadd(day,-1,@BegMonth)
left join ra on ra.izm0 = n.izm0 and ra.izm1 = n.izm1 and @BegMonth <= ra.Datedoc
and rg.Datedoc <= @DateDoc
where not exists (select * from #in_temp i where n.izm0 = i.izm0 and n.izm1
= i.izm1)
И наконец получение самого остатка:
Select d.izm0,d.izm1,sum(t.res-(1-2*ra.debkred)*ra.res)
from #in_temp d
left join #temp_tot t on t.izm0 = d.izm0 and d.izm1 = d.izm1 and t.period =
@DateDoc
left join ra on ra.izm0 = d.izm0 and ra.izm1 = d.izm1 and ra.Datedoc = @Datedoc
and ra.Time > @TimeDoc
P.S. Запросы не проверялись на правильность составления! Поскольку подсистема БУ очень похожа на ОУ в хранении итогов, то данную методки легко можно перенести и на БУ, нужно лишь учесть, что в таблице добавляется дополнительное измерение счет, а также то, что расчет остатка осущствляется при помощи нескольких полей таблицы итогов (остаток и обороты).
Шемякин Павел, июль 2002. Оригинал статьи на http://1csql.virtualave.net
Комментарии
1или я чтото не уловил?