Hare.ru @ Коллективный разум / Hare.ru @ Дикое место

Архив hare.ru 
Мысли, конвертированные в текст

Концептуальные работы


Все статьи раздела

Групповое проведение документов. Ускоряемся?

Павел Шемякин (toypaul) (июль 2002)

Оригинал статьи: 1sql.virtualave.net

Данная методика является чистой воды теорией (пока), так что использовать описанный алгоритм на живых базах категорически не рекомендуется.

Довольно часто при решении задач в системе 1С:Предприятие возникает необходимость в групповом проведении документов. Одна из основных причин – исправление ошибок, которые могут возникнуть за какой-либо прошлый период. Иногда это является частью алгоритма работы. Например, в течение одного месяца ведется только количественный оперативный учет, а затем, в конце месяца, формируются бухгалтерские проводки и суммовой учет в оперативной подсистеме. Есть еще много причин, но статья посвящена не этому.

Цель статьи – разобраться, как можно ускорить выполнение этой задачи в системе 1С:Предприятие 7.7 (версия для SQL). Довольно часто приходится слышать, что в этой версии групповое проведение работает значительно медленнее, чем в DBF-версии.

Сразу хочу заметить, что групповое проведение в 1С можно реализовать несколькими способами: своей обработкой, через меню «Операции – Проведение документов». При этом второй способ, как вы наверное могли заметить, работает быстрее, нежели проведение документов с помощью обработки (по сути это перепроведение документов с помощью метода Провести).

Дело в том, что, как мне кажется, в этом случае система оптимизирует расчет остатков при проведении документов. Во всяком случае, наблюдения за системой через Profiler, подтверждают такой вывод. Я не обнаружил сложных вычислений остатков, а также увидел некоторые «подозрительные» манипуляции с таблицей остатков. Долго разбираться не хватило сил – при проведении всего двух документов список сообщений в Profiler состоял из нескольких десятков страниц.

Хотелось бы также заметить, что и в первом случае возможно оптимизированное проведение документов. Для этого можно использовать метод Актуальность. Прочтя документацию по V7 и дополнительные материалы, я, к сожалению, не нашел нигде упоминания об использовании этого метода для оптимизации проведения документов оперативного учета. То есть применять его (следуя документации) можно, но только для проведения документов использующих бухгалтерскую компоненту. Пример использования данного метода можно посмотреть в стандартной обработке бухгалтерских конфигураций 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;Документ Î {Д|(Д >= ДИ) и
(Д.Дата <= ДИ.Дата)}
КонецЦикла

Прошу прощения, если допустил какие-то грубые (или не очень грубые) ошибки в описании алгоритма таким образом. Очень хотелось записать алгоритм как можно короче – использование математического языка в этом случае очень помогает. Самое главное кто с ним знаком, то все понятно. Не ручаюсь за правильность использования данного языка – давно не было практики, а в учебники как-то лень заглядывать (для начала их еще найти надо ;-).

Пояснения для математически неграмотных ;-):

Таблица остатков и движений суть функции по измерениям и периоду (дате). Значение функции в общем случае числовой вектор. В моем случае ресурс один. Документ рассматриваем как множество кортежей, то есть реляционная таблица. Считаем, что все значения реквизитов и значение даты документа хранятся в одной таблице. Промежуточная таблица итогов похожа на таблицу остатков с той лишь разницей, что периодичность их хранения один день. Функция расчета остатка – понятно без объяснений (так считает остатки сама V7). На выходе мы должны получить функцию аналогичной Ф, результат вычисления которой для последовательности документов ДП будет более оптимальным с точки зрения производительности.

В процессе формулировки последнего предложения меня посетила мысль, что результатом данного алгоритма должна стать не более оптимальная функция, а просто функция. Не знаю есть ли такое понятие в математическом аппарате как оптимальность функции по отношению к другой, но то что существует оценка алгоритма, реализующего функцию это точно. К сожалению я не владею данным аппаратом, но думаю, что тем, кто с этим знаком не составит труда произвести оценку первого алгоритма реализующего функцию Ф (то есть простой ее вариант, рассмотренный в секции вспомогательные данные) и алгоритма реализующего эту же функцию, но названную как ФОПТ. То есть функция одна, а алгоритма два.

Пояснения к самому алгоритму. Для каждого документа мы находим множество измерений (НадоСчитать), которые отсутствуют во временной таблице. Затем в таблице итогов выбираем остатки, которые максимально близки к необходимым для расчета, содержащимся в множестве НадоСчитать. Добавляем во временные итоги вновь рассчитанные остатки. Первая часть по множеству измерений, которые «отталкиваются» от таблицы временных итогов, вторая часть рассчитывается с помощью «неоптимизированной функции». В конце считаем остатки на документ – из остатка на дату документам, которые берутся из временных итогов, вычитаем лишние движения.

Вот и все. Остается произвести оценку алгоритма, как было описано выше, и затем подтвердить ее на практике.

Приложение. Реализация на T-SQL

Здесь я рассмотрю некоторые шаги алгоритма, реализованные на T-SQL. Структура таблиц (похожа на 1С, но не совсем):

Регистр

Итоги (rg)
izm0
izm 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.

Запросы не проверялись на правильность составления! Поскольку подсистема БУ очень похожа на ОУ в хранении итогов, то данную методки легко можно перенести и на БУ, нужно лишь учесть, что в таблице добавляется дополнительное измерение счет, а также то, что расчет остатка осущствляется при помощи нескольких полей таблицы итогов (остаток и обороты).

Партнеры:


Также может быть интересно:

Канал Россия 1 на http://spbtvonline.ru/
   
 Сайт поддерживается за счет партнеров: Справка бти форма 1а по материалам сайта.
:::... Сайт содержит архив двух версий hare.ru Карта сайта