почему ARD работает медленнее, чем интерфейс?

Программирование на Атлантисе (VIP, FCOM, ARD), FastReport

Модераторы: m0p3e, edward_K, Модераторы

Ответить
s2176
Местный житель
Сообщения: 473
Зарегистрирован: 29 мар 2005, 17:49
Откуда: Новосибирск

почему ARD работает медленнее, чем интерфейс?

Сообщение s2176 »

Мне нужно получить отчет по налогам на ФОТ в разрезе людей и видов оплат. Т.е., каждая строчка этого отчета - сколько всего налогов приходится на конкретный вид оплаты конкретного работника.
Таблица Perevod большая, за месяц в ней появляется около 200 000 записей, а в отчете в итоге должно быть около 20 000 строк.
Я писала похожий интерфейс по этой таблице, только там группировка была другая не по видам оплат, а по счету/субсчету, так он у меня отрабатывал минут за 20. А отчет крутится 1,5 часа...
В интерфейсе я использовала свои временные таблички. Решила и в отчете пойти по этому же пути, но ... увы.
Собственно разница между отчетом и интерфейсом заключется только в том, что в отчете при работе с таблицами я использую
.{ table 'моя_таблица'
.}
а в интерфейсе
loop
{ }
Но почему же такая разница в скорости работы???
Hmyrii
Постоянный гость
Сообщения: 62
Зарегистрирован: 07 июн 2006, 08:23

Сообщение Hmyrii »

Полтора часа это и в самом деле неприемлимо долго. Я делел отчет, там фигурировала таблица Oborot. Таблица не из мальеньких. Был множественный выбор счетов, для каждгого из которых свои филтры по субсчетам и КАУ. Отчет выгружался не в стандартный текстовичок в в шаблон екселевский. Там где то так же выходит, 20-30 тысяч записей, с группировками всякими разными. Отрабатывает 10-20 минут. Так что наверное что то не оптимально нописано. Дай связку таблиц и напиши по подробней о фильтрах и что хочешь добится, люблю заниматься оптимизацией отчетов 8-)
s2176
Местный житель
Сообщения: 473
Зарегистрирован: 29 мар 2005, 17:49
Откуда: Новосибирск

Сообщение s2176 »

Вот чуть-чуть урезанный текст:

.form 'Налоги за прошедший период с МВЗ и КБК_'
.ard
.group'Отчеты для ОТИЗ'
.var
god, mes: string[6];
dd,yy,mm: word;
dostup: word;
nrec_pers: comp;
_bklass: string;
vv: longint;
.endvar

.create view tkvo1
as select klvidopl.vidoplp, exclassseg.value, exclassseg.name
from klvidopl,exclassseg
where ((klvidopl.nrec==exclassval.crec
and exclassval.cclassseg==exclassseg.nrec and 12==exclassval.classcode
and 15010==exclassval.wtable
))
;

.create view tper
as select perevod.tabn, perevod.vidopl, perevod.sumper, perevod.tperson, perevod.vidper
from perevod
where ((yy==yearn and mm==mesn and 13>>vidper))
;

.create view tesn
as select tab_esn.tabn, tab_esn.vidopl, tab_esn.summa,
tmpklvop.nvidopl, tmpklvop.bklass, tmpklvop.nbklass
from tab_esn(ind1), tmpklvop
where ((tab_esn.vidopl == tmpklvop.vidoplp ))
;
.create view t_vo
as select tmpklvop.*
;
.create view t_esn
as select tab_esn.*
;

.begin
yy:=year(dGetTune('UP.DATOTCH')); mm:=month(dGetTune('UP.DATOTCH'))
if mm=1 then { mm:=12; yy:=yy-1; }
else mm:=mm-1;
god:=string(yy); mes:=string(mm);
rundialog(getmesYear,god,mes);
dostup:=0; dd:=last_day(date(1,mm,yy))
if sGetTune('USER.DESGR')='OTIZ' dostup:=1;
if pr_CurUserAdmin=true then dostup:=1;
end.
.{ table 'tkvo1'
.begin
if tkvo1.exclassseg.value<>''
{ if substr(tkvo1.exclassseg.value,1,6)='A26010'
then _bklass:=substr(tkvo1.exclassseg.value,1,4)+'2'+substr(tkvo1.exclassseg.value,6,3)
else _bklass:='A2602060';
}
else _bklass:='';
t_vo.insert tmpklvop
set vidoplp:=tkvo1.klvidopl.vidoplp, nvidopl:=tkvo1.klvidopl.nvidopl, bklass:=_bklass,
nbklass:=tkvo1.exclassseg.name;
end.
.}
.begin
startNewVisual(vtNumericVisual,vfTimer,'выгpyжаю налоги.....',1);
end.
.fields
mm+'/'+yy
.endfields
Период - ^
.{?internal; dostup=1
.{ table 'tper'
.begin
nrec_pers:=tper.perevod.tperson; vv:=tper.perevod.vidopl;
if getfirst tab_esn where ((nrec_pers==tab_esn.tperson and vv==tab_esn.vidopl)) = tsOk
then { t_esn.update from tab_esn
set summa:=tab_esn.summa+tper.perevod.sumper;
}
else { t_esn.insert tab_esn
set tabn:=longint(tper.perevod.tabn), tperson:=comp(tper.perevod.tperson),
vidopl:=longint(tper.perevod.vidopl), summa:=tper.perevod.sumper;
};
NextVisual();
end.
.}
.begin
stopVisual('',0);
end.
.fields
string(tesn.tab_esn.tabn)
string(tesn.tab_esn.vidopl)
tesn.tmpklvop.nvidopl
doubletostr(tesn.tab_esn.summa,'[|-]36666667.8888')
tesn.tmpklvop.bklass
tesn.tmpklvop.nbklass
.endfields
.{ table 'tesn'
^ ^ ^ ^ ^ ^
.}
.}
.endform

Очень медленно работает цикл по таблице Perevod
edward_K
Заслуженный деятель интернет-сообщества
Сообщения: 5188
Зарегистрирован: 29 мар 2005, 17:49
Откуда: SPB galaxy spb

Сообщение edward_K »

1. использование
getfirst tab_esn where ((nrec_pers==tab_esn.tperson and vv==tab_esn.vidopl)) = tsOk
ведет к перестроению всех логических таблиц - см хелп.
избавтесь от этого.
2. можно обойтись без цикла table
а добавить програмный блок
.begin
tper_loop fullcache
{
}
end.
на sql или оракле это может привести к существеннному ускорению.
хотя столкнулся что просто _loop могет и медленей работать в десятки раз чем getfirst - getnext.
s2176
Местный житель
Сообщения: 473
Зарегистрирован: 29 мар 2005, 17:49
Откуда: Новосибирск

Сообщение s2176 »

вообще-то tab_esn - не логическая таблица, а просто временная, ее структура описана в проекте.
А чем можно заменить getfirst? Ведь в таблице tab_esn мне нужно накопить суммы... Как по-другому найти нужную запись и откорректировать ее?
Кстати, про _loop. В интерфейсе я вместо
.{ table
.}
использую _loop, и все работает очень быстро, с тем же getfirst'ом
edward_K
Заслуженный деятель интернет-сообщества
Сообщения: 5188
Зарегистрирован: 29 мар 2005, 17:49
Откуда: SPB galaxy spb

Сообщение edward_K »

смысл не в getfirst а в использовании where :).
сам getfirst пашет быстро. Можно еще fastfirstrow добавить.
выдержка из доки.
================
Рекомендации по написанию прикладного кода для более оптимального использования возможностей SQL платформ

1. Стоит учитывать, что Лот разбивает логическую таблицу на подзапросы. Критерием разбивки является уникальный ключ. SQL драйвера оперируют в рамках подзапрсов. Когда речь идет о перестроении структуры запроса или потере кэша имеются ввиду подзапросы. Снимание или наложение bounds, order может приводить к изменению структуры подзапрсов, что в свою очередь приводит к потере кэша данных и кэша SQL-запросов.

2. Желательно все реляционные связи, особенно по уникальнык ключам, описывать в логической таблице. В этом случае, на SQL-платформах возможна их выборка за один запрос. Реально, врема выборки слабо зависит от размера запроса. Посему, две таблицы связанные реляционными отношением по уникальному ключу, описанным в логической таблице при выбоке замут время в два раза меньшее нежели комбинация выборка из первой таблицы + GetEqual по второй. Еще хуже будет работь в этом случае наложение и снимание боундов внутри цикла. Посему, очевидно, что алгоритмы, написанные на Pascal, без использования Лота будут работать хуже всего.

3. Конструкции типа GetFirst ... where, GetNext ... where работают менее оптимально чен комбинация Bounds+GetFirst, GetNext.Если, конечно, боунды наложить до, а снять после цикла. Это связано с тем, что конструкция GetNext ... where КАЖДЫЙ раз перестраивает структуру подзапроса. Сейчас, для удобства прикладного программирования, разрабатывается оператор цикла по таблице.

4. Модификации (update, delete, insert) приводят к частичной (или полной) потере кэша для ВСЕХ подзапросов, содержащии модифицируемую таблицу. Здесь имеются ввиду не только синонимы физической таблицы в рамках одной логической таблицы, но и другие активные логические таблицы (открытые интерфейсы и т.д.), содержащие в себе эту физическую таблицу.
s2176
Местный житель
Сообщения: 473
Зарегистрирован: 29 мар 2005, 17:49
Откуда: Новосибирск

Сообщение s2176 »

Но в интерфейсе-то все точно так же, как в отчете....
А работает в 10 раз быстрее... Я вот это пытаюсь понять.

Я итерфейс сделала на основе этого же отчета, не мудрствуя лукаво, просто заменила
.{ table }. на _loop....
В интерфейсе заполняю свои временные таблицы, и из него уже получаю отчет.
Кто сказал, что бесполезно биться головой об стену?!
s2176
Местный житель
Сообщения: 473
Зарегистрирован: 29 мар 2005, 17:49
Откуда: Новосибирск

Сообщение s2176 »

Поставила bounds стало быстрее, но увлекшись ускорением, не заметила, что оказывается самый первый цикл по tklvo не отрабатывает почему-то.....
вот полный текст:

.form 'Налоги за прошедший период с МВЗ и КБК_'
.ard
.group'Отчеты для ОТИЗ'
.var
god, mes: string[6];
dd,yy,mm: word;
dostup: word;
nrec_pers: comp;
app_date, dis_date: date;
dat_uv: date;
_bklass: string;
vv: longint;
.endvar
.create view tklvo
as select klvidopl.vidoplp, klvidopl.nvidopl, exclassseg.value, exclassseg.name
from klvidopl, exclassseg
where ((klvidopl.nrec==exclassval.crec
and exclassval.cclassseg==exclassseg.nrec and 12==exclassval.classcode
and 15010==exclassval.wtable
))
;
.create view tper
as select perevod.tabn, perevod.vidopl, perevod.sumper, perevod.tperson, perevod.vidper
from perevod
where ((yy==yearn and mm==mesn and 12==vidper))
;
.create view tdi
as select appointdate, dismissdate, person, dateend,
catalogs.name, catalogs.code, cat.name, klkatego.naikat
from appointments(APNTBYPERSON), catalogs cat, catalogs, klkatego
where ((appointments.department==catalogs.nrec and post==cat.nrec and
empcategory==klkatego.nrec and
date(dd,mm,yy)>>=appointdate(noindex)
))
;
.create view tesn
as select tab_esn.tabn, tab_esn.vidopl, tab_esn.summa,
tmpklvop.nvidopl, tmpklvop.bklass, tmpklvop.nbklass,
persons.fio, tmpapp.appdate, tmpapp.disdate, persons.disdate,
tmpapp.code, tmpapp.name, tmpapp.dolgn, tmpapp.kateg
from tab_esn(ind1), tmpklvop, tmpapp
where ((tab_esn.tperson == tmpapp.tperson and tab_esn.vidopl == tmpklvop.vidoplp and
tab_esn.tperson == persons.nrec
))
;
.create view t_vo
as select tmpklvop.*
;
.create view t_app
as select tmpapp.*
;
.create view t_esn
as select tab_esn.*
bounds bte = nrec_pers==tab_esn.tperson and vv==tab_esn.vidopl
;

.begin
yy:=year(dGetTune('UP.DATOTCH')); mm:=month(dGetTune('UP.DATOTCH'))
if mm=1 then { mm:=12; yy:=yy-1; }
else mm:=mm-1;
god:=string(yy); mes:=string(mm);
rundialog(getmesYear,god,mes);
dostup:=0; dd:=last_day(date(1,mm,yy))
if sGetTune('USER.DESGR')='OTIZ' dostup:=1;
if pr_CurUserAdmin=true then dostup:=1;
end.
.{ table 'tklvo'
.begin
if tklvo.exclassseg.value<>''
{ if substr(tklvo.exclassseg.value,1,6)='A26010'
then _bklass:=substr(tklvo.exclassseg.value,1,4)+'2'+substr(tklvo.exclassseg.value,6,3)
else _bklass:='A2602060';
}
else _bklass:='';
t_vo.insert tmpklvop
set vidoplp:=tklvo.klvidopl.vidoplp, nvidopl:=tklvo.klvidopl.nvidopl, bklass:=_bklass,
nbklass:=tklvo.exclassseg.name;
message(_bklass)
end.
.}
.begin
startNewVisual(vtNumericVisual,vfTimer,'выгpyжаю назначения.....',1);
end.
.{ table 'tdi'
.begin
nrec_pers:=tdi.person; app_date:=tdi.appointdate;
if tdi.dismissdate<>date(0,0,0)
then dat_uv:=tdi.dismissdate
else dat_uv:=tdi.dateend;
if dat_uv=date(0,0,0) then dat_uv:=date(31,12,2100);
if getfirst tmpapp where((nrec_pers==tmpapp.tperson)) = tsOk
{ if app_date<=date(16,mm,yy)
then { if dat_uv>=tmpapp.disdate
t_app.update tmpapp
where((nrec_pers==tmpapp.tperson))
set code:=tdi.catalogs.code, name:=tdi.catalogs.name, appdate:=app_date, disdate:=dat_uv,
kateg:=tdi.klkatego.naikat, dolgn:=tdi.cat.name;
};
}
else t_app.insert tmpapp
set tperson:=tdi.person, code:=tdi.catalogs.code, name:=tdi.catalogs.name,
dolgn:=tdi.cat.name, kateg:=tdi.klkatego.naikat,
appdate:=tdi.appointdate, disdate:=dat_uv;
NextVisual();
end.
.}
.begin
stopVisual('',0);
startNewVisual(vtNumericVisual,vfTimer,'выгpyжаю налоги.....',1);
pushBounds(tbbte);
end.
.fields
mm+'/'+yy
.endfields
Период - ^
.{?internal; dostup=1
.{ table 'tper'
.begin
nrec_pers:=tper.perevod.tperson; vv:=tper.perevod.vidopl;
if t_esn.getfirst tab_esn = tsOk
then { t_esn.update from tab_esn
set summa:=tab_esn.summa+tper.perevod.sumper;
}
else { t_esn.insert tab_esn
set tabn:=longint(tper.perevod.tabn), tperson:=comp(tper.perevod.tperson),
vidopl:=longint(tper.perevod.vidopl), summa:=tper.perevod.sumper;
};
NextVisual();
end.
.}
.begin
popBounds(tbbte);
stopVisual('',0);
end.
.fields
string(tesn.tab_esn.tabn) string(tesn.tab_esn.vidopl)
tesn.tmpklvop.nvidopl
doubletostr(tesn.tab_esn.summa,'[|-]36666667.8888')
tesn.tmpklvop.bklass tesn.tmpklvop.nbklass
tesn.persons.fio tesn.tmpapp.kateg
if(tesn.tmpapp.appdate>date(1,mm,yy),tesn.tmpapp.appdate,'')
if(tesn.tmpapp.disdate<=date(dd,mm,yy),tesn.tmpapp.disdate,'')
tesn.tmpapp.code tesn.tmpapp.name tesn.tmpapp.dolgn
if(tesn.persons.disdate<>date(0,0,0),tesn.persons.disdate,'')
.endfields
.{ table 'tesn'
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
.}
.}
.endform

у меня такое впечатление, что первые два цикла мешают друг другу
edward_K
Заслуженный деятель интернет-сообщества
Сообщения: 5188
Зарегистрирован: 29 мар 2005, 17:49
Откуда: SPB galaxy spb

Сообщение edward_K »

1. избавтесь вообще от where в програмных блоках
в тои числе и в update.
2. popBounds правильней вызывать так
tesn.popBounds(tesn.tbbte);
3.к классиф. лучше обратиться как
15 == katos.tidk and 1 == katos.isleaf
and 3000 == EXCLASSNAME.wtable
and 'Вид ос' ==EXCLASSNAME.name
and EXCLASSNAME.classcode == exclassval.classcode
and 3000 == exclassval.wtable
and katos.nrec ==exclassval.crec
and exclassval.cclassseg == exclassseg.nrec
- так вы не будете завязаны на базу.
3. а в сапорте то select выполняется?
4. а в цикл вообще не попадает или данные не те?
s2176
Местный житель
Сообщения: 473
Зарегистрирован: 29 мар 2005, 17:49
Откуда: Новосибирск

Сообщение s2176 »

в Supporte select конечно выполняется (обижаете?).
Цикл как будто вообще не выполняется, я специально там message вставила. А если я избавляюсь от цикла по tdi, то он (по tvo) начинает работать.
edward_K
Заслуженный деятель интернет-сообщества
Сообщения: 5188
Зарегистрирован: 29 мар 2005, 17:49
Откуда: SPB galaxy spb

Сообщение edward_K »

попробуйте в програмном блоке перед ним пройтись по этой вьющке
через if tklvo.getfirst=0 { do {} while tklvo.getnext=0 ;}
или цикл сделать не по вьющке а по конкретной табле - только не забудьте тогда делать getfirst по подчиненным таблам.
s2176
Местный житель
Сообщения: 473
Зарегистрирован: 29 мар 2005, 17:49
Откуда: Новосибирск

Сообщение s2176 »

делала, не работает(((
вернее, я делала не перед ним, а вместо него
edward_K
Заслуженный деятель интернет-сообщества
Сообщения: 5188
Зарегистрирован: 29 мар 2005, 17:49
Откуда: SPB galaxy spb

Сообщение edward_K »

что и даже if tklvo.getfirst klvidopl=0 message('!!!!') else message('????') ; не пашет?
s2176
Местный житель
Сообщения: 473
Зарегистрирован: 29 мар 2005, 17:49
Откуда: Новосибирск

Сообщение s2176 »

хм, решила еще раз попробовать, сейчас конструкция

if tklvo.getfirst klvidopl = tsOk
{ do { message('!!!')

} while tklvo.getnext klvidopl = tsOk
}

отработала.... Но как понять логику????
Кто сказал, что бесполезно биться головой об стену?!
s2176
Местный житель
Сообщения: 473
Зарегистрирован: 29 мар 2005, 17:49
Откуда: Новосибирск

Сообщение s2176 »

похоже, что я в первый раз вместо getnext написала getlast... потому и не работало.... сейчас чуть так же не получилось (заработалась)
Сейчас работает!
Но почему не хочет работать обычный цикл? Этому есть какие-то объяснения?
Кто сказал, что бесполезно биться головой об стену?!
Ответить