OnCalcField ?
9 отговора
191 прегледа
OnCalcField ?
Здравейте, аз съм твърде начинаещ, и доста се чудя над едно нещо.
Чета данни от IBDataSet. Имам две числови полета, които не ги извеждам в
грида си, а вместо тях използувам две стрингови полета които са от тип
fkCalculated. Тоест, вместо числа , OnCalcField пресмята стойността
на стринговите полтеа, които извеждам. Стават едни глупости.
1. OnCalcField се разхожда през целия датасет когато променям
стойниост на някое от изчисляващите полета.
2. Тези fkCalculated Fielda (TStringField) са 2. Като пресметне едното,
ВТОРОТО отива на майната си. Добива стойност на стринга такава,
сякащ числовото поле от което се смята е станало 0.
Като направя фреш на датасета всичко си отива на мястото.
Взех да си мисля дали да не търся друга алтернатива да показвам в грида,
изчислямеи колони...
10x предварително
Чета данни от IBDataSet. Имам две числови полета, които не ги извеждам в
грида си, а вместо тях използувам две стрингови полета които са от тип
fkCalculated. Тоест, вместо числа , OnCalcField пресмята стойността
на стринговите полтеа, които извеждам. Стават едни глупости.
1. OnCalcField се разхожда през целия датасет когато променям
стойниост на някое от изчисляващите полета.
2. Тези fkCalculated Fielda (TStringField) са 2. Като пресметне едното,
ВТОРОТО отива на майната си. Добива стойност на стринга такава,
сякащ числовото поле от което се смята е станало 0.
Като направя фреш на датасета всичко си отива на мястото.
Взех да си мисля дали да не търся друга алтернатива да показвам в грида,
изчислямеи колони...
10x предварително
Ако IBDataSet имат Fields, ползвaj OnGetText
Нямам опит с IB, но предполагам, че и IBDataSet имат Fields.
Ако е така, зарежи ОнКалк на DataSet-а и прехвани ОнГетТекст на tFields, например така:
{}procedure TForm.Query1FieldGetText(Sender: TField; var Text: String; DisplayText: Boolean);
begin
// В твоя случей Sender е TIntegerField
Text:='Както искаш го прави: '+Sender.AsString;
end;
Ако нямаш явно зададени tFields можеш да го направиш и в RunTime, но като за теб е по-лесно да си ги определиш по време на дизайна.
Ако е така, зарежи ОнКалк на DataSet-а и прехвани ОнГетТекст на tFields, например така:
{}procedure TForm.Query1FieldGetText(Sender: TField; var Text: String; DisplayText: Boolean);
begin
// В твоя случей Sender е TIntegerField
Text:='Както искаш го прави: '+Sender.AsString;
end;
Ако нямаш явно зададени tFields можеш да го направиш и в RunTime, но като за теб е по-лесно да си ги определиш по време на дизайна.
OnCalcField се разхожда през целия датасет ли?!
Какво имаш предвид под OnCalcField се разхожда през целия датасет? В OnCalcFields не трябва да се местиш по DataSet-а...
Не знам какво точно си направил (ако искаш вземи публикувай малко код), но правилният подход трябва да е нещо такова:
Животът не се мери с броя вдишвания, а с моментите, които спират дъха ни!
Не знам какво точно си направил (ако искаш вземи публикувай малко код), но правилният подход трябва да е нещо такова:
procedure TfrmMyForm.tbMyTableCalcFields(DataSet: TDataSet);
begin
DataSet.FieldByName('STRING_FIELD_1').AsString := 'Конвертирано число 1: ' + DataSet.FieldByName('INTEGER_FIELD_1).AsString;
DataSet.FieldByName('STRING_FIELD_2').AsString := 'Конвертирано число 2: ' + DataSet.FieldByName('INTEGER_FIELD_2).AsString;
end;
Животът не се мери с броя вдишвания, а с моментите, които спират дъха ни!
Харесва си ми :)
Няма такова нещо. Харесва ми и активно го ползвам
Просто малко се ошашках от репликата на човека... Някак си останах с впечатление, че в OnCalcFields се разхожда напред-назад из dataset-а :)
Ще видим като сложи малко сорс какво е било всъщност...
Животът не се мери с броя вдишвания, а с моментите, които спират дъха ни!
Просто малко се ошашках от репликата на човека... Някак си останах с впечатление, че в OnCalcFields се разхожда напред-назад из dataset-а :)
Ще видим като сложи малко сорс какво е било всъщност...
Животът не се мери с броя вдишвания, а с моментите, които спират дъха ни!
Re: Харесва си ми :)
Проблема май се състои в това, че явно нейде има грид който требе да се рефрешне ;-))) - и познай какво става - то4но тогава се "разхожда" из вси4ки нужни редове май - не съм сигурен ама може би е това. Може и само операция която изважда всички редове от таблицата - да прави това.
---
Колкото повече, толкова повече.
---
Колкото повече, толкова повече.
Уточнение, много приказки.
Здравейте,
Моля за извинение, дълго е писанието, отнема време.
Прави сте,че трябва да се покаже и соурс, за да се види проблема. Аз обаче
се притеснявах да ни ви отнемам време с проблем, който може да е слабо
професионален. Но ми се струва, че може да има не само грешки, свързани
с някакъв OnCalcField, но и по сериозни непознания, опиращи дори до сървески
грижи. Затова ще ви помоля за малко внимание, щото ще съм малко подробен.
Извинете, че кода е на "Ц++" (CBuilder ), но това в случая нема никакво значение.
Пък и един добър програмист не му пука.
Показвам ви само значещата част на проекта над който се уча, за съжаление
съвсем сам (нямам колеги). Базата не е малка, и показвам една част от нея.
Работи се в мрежа.
I. Три InterBase таблици - TIBTable class.
1. IBTEmp - таблица са данни за хора с пъричен ключ EGN ( string(10)).
От тук вадя единствено по ключ (Lookup), име
и код на професия. ( около 1200-1500 калпака)
2. IBTProf - таблица с професии с първичен ключ PROFID (char(4)).
От тук вадя единствено по ключ (Lookup) име на
професия. ( По държавен класификатор не повече от 1000 )
3. TIBEmpJournal - Това е работната таблица (Журнал), с която се описва идването на някой - кога, защо. Тази таблица се пълни много.
Полета Тип (InterBase)
--------------------------------------------------------------------------------------
PDATE Date
EGN char(10) Primary Key PDATE,EGN,ENTRY
ENTRY SmallInt
INREASON SmallInt
OUTREASON SmallInt
...И други няколко маловажни.
II. За да обслужвам журнала използува динамичен dataset, с параметри, така че
да го отсичам за период.
IBDSempPortJournal - (TIBDataSet class)
select * from EMP_PORTER_JOURNAL
where PDATE>=:dFirst and PDATE<=:dLast
Двата параметъра са тип TDateTime. Генерират се полета
Поле Тип
-----------------------------------------------------------------------------------------
PDATE Date (Pesristent)
EGN char(10) (Persistent)
ENTRY SmallInt (Persistent)
INREASON SmallInt (Persistent)
OUTREASON SmallInt (Persistent)
EMPNAME char(40) (Lookup , Field Editor)
PROFID char(4) ( Lookup , Field Editor)
PROFNAME char(20) ( Lookup , Field Editor)
INREASONTEXT char(20) (fkCalculated,Field Editor)
OUTREASONTEXT char(20) (fkCalclulated,Field Editor )
Последните две полета са fkCalculated, защото заемат максимум 10-15
стойности според пресмятащото ги поле, и не видях причина да правя Lookup
таблица. (и да нямам грижи)
III. Тази IBDSEmpPortJournal ( TIBDataSet ) я мятам в едит DBGrid с колони:
| PDATE | EMPNAME | INREASONTEXT | OUTREASONTEXT |
В този грид, основната ми грижа е да определям текста , в стринговите fkCalculated - INREASONTEXT чрез числовото INREASON.
fkCalculated - OUTREASONTEXT чрез числовото OUTREASON
и да записвам САМО числовите полета.
ИЗПОЛЗУВАМ имента на полетата at Dessign Time ( Fiedl Editor).
void __fastcall TJournal::OnMyCalcField(TDataSet* Dataset)
{
switch(INREASON->AsInteger) {
case 1:
INREASONTEXT->AsString=" Идва да товари стока ";
break;
case 2:
INREASONTEXT->AsString=" Носи документ ";
break:
и така нататък
}
switch(OUTREASON->AsInteger) {
case 5:
OUTREASONTEXT->AsString=" Взе стоката и бега... ";
break;
case 6:
INREASONTEXT->AsString=" Взе документите и бега... ";
break:
и така нататък
}
}.
ПРОБЛЕМИ.
По принцип нямам никакви хаотични обръщения към Events, които да правят
щуротии. Опитвах с cached/ "не"chaced updates.
Кода е 5 реда и половина и в него категорично не се вижда грешка.
Сложих едно "наблюдение" във функцията "OnMyCalcField". Оказва се че при
всяка редакция, ( дори и на PDATE ), функцият се разхожда като луда през
всички записи в рекордсета, а не само в текущо редактирвания ред. И да видите
каква интересна картинка !
Представете си, че е избрана една Cell от грида, и след това редактирам
стойността в нея. След редактирането се вижда как през тази цел, като на
екран, се "превъртат" всички стойности на избраното поле (колона), за
целия рекордсет. Нещо, като "прецъкването" на брояча на бензиноколонка.
В същата колона, другите Cell не "мигват".
Иначе казано, ако имам примерно 2 fkCalculated полета, и 100 записа,
броят на обръщенията към OnCalckField e N=2 *100 при режим на
начално зареждане , и N>2 *100 при корекция на КОЕТО и да е поле.
UpdateMode (upWhereAll ), не се оказа причината. ( сякаш ?)
N >= 2 * 100
Има и друго. Скоростта с която работи търсенето:
TLocateOptions tl;
tl.Clear() ;
tl<<loCaseInsensitive;
IBDSEmpPortJournal->Locate("PDATE;EGN,ENTRY", VarArrayOf(.........), tl);
тази скорост просто е абсурдна. Това не ми прилича на търсене по ключ, а филтриране.
Три дена път с камили.
Вчера смених компонента TIBDataSet със TIBClientDataSet. Скоростта на
търсене се увеличи 100-на пъти !!!
Append() е със скоростта на охлюв.Аз си позволявам следното:
IBDSEmpPortJournal->Append();
PDATE->Value=...................;
EGN->Value=..........;
ENTRY->AsInteger=.........;
INREASON->AsInteger=........;
IBDSEmpJournal->ApplyUpdates()
if( !MyTransaction->InTransaction )
MyTransaction->StartTransaction();
MyDataBase->CommitRetaining();
Абе мисля си, че типовете данни нещо не са в ред, TIBDataSet може да е
неподходяща или нещо в първичен ключ. Когато съм пробвал някои
работи с TTable, такива грижи не е имало.
Извинете ако ви надух главата. Тук стана едни майтап.
Имам познат, който преди години е работил с програмиране. Той се ебава така:
"...Иди в музея и ми докарай едно AT 286. Шъ ти напиша код на Clipper, който
работи 1000 пъти по-бързо и N-пъти по надеждно. Ама ще ми туриш и DOS..."
10x.
Моля за извинение, дълго е писанието, отнема време.
Прави сте,че трябва да се покаже и соурс, за да се види проблема. Аз обаче
се притеснявах да ни ви отнемам време с проблем, който може да е слабо
професионален. Но ми се струва, че може да има не само грешки, свързани
с някакъв OnCalcField, но и по сериозни непознания, опиращи дори до сървески
грижи. Затова ще ви помоля за малко внимание, щото ще съм малко подробен.
Извинете, че кода е на "Ц++" (CBuilder ), но това в случая нема никакво значение.
Пък и един добър програмист не му пука.
Показвам ви само значещата част на проекта над който се уча, за съжаление
съвсем сам (нямам колеги). Базата не е малка, и показвам една част от нея.
Работи се в мрежа.
I. Три InterBase таблици - TIBTable class.
1. IBTEmp - таблица са данни за хора с пъричен ключ EGN ( string(10)).
От тук вадя единствено по ключ (Lookup), име
и код на професия. ( около 1200-1500 калпака)
2. IBTProf - таблица с професии с първичен ключ PROFID (char(4)).
От тук вадя единствено по ключ (Lookup) име на
професия. ( По държавен класификатор не повече от 1000 )
3. TIBEmpJournal - Това е работната таблица (Журнал), с която се описва идването на някой - кога, защо. Тази таблица се пълни много.
Полета Тип (InterBase)
--------------------------------------------------------------------------------------
PDATE Date
EGN char(10) Primary Key PDATE,EGN,ENTRY
ENTRY SmallInt
INREASON SmallInt
OUTREASON SmallInt
...И други няколко маловажни.
II. За да обслужвам журнала използува динамичен dataset, с параметри, така че
да го отсичам за период.
IBDSempPortJournal - (TIBDataSet class)
select * from EMP_PORTER_JOURNAL
where PDATE>=:dFirst and PDATE<=:dLast
Двата параметъра са тип TDateTime. Генерират се полета
Поле Тип
-----------------------------------------------------------------------------------------
PDATE Date (Pesristent)
EGN char(10) (Persistent)
ENTRY SmallInt (Persistent)
INREASON SmallInt (Persistent)
OUTREASON SmallInt (Persistent)
EMPNAME char(40) (Lookup , Field Editor)
PROFID char(4) ( Lookup , Field Editor)
PROFNAME char(20) ( Lookup , Field Editor)
INREASONTEXT char(20) (fkCalculated,Field Editor)
OUTREASONTEXT char(20) (fkCalclulated,Field Editor )
Последните две полета са fkCalculated, защото заемат максимум 10-15
стойности според пресмятащото ги поле, и не видях причина да правя Lookup
таблица. (и да нямам грижи)
III. Тази IBDSEmpPortJournal ( TIBDataSet ) я мятам в едит DBGrid с колони:
| PDATE | EMPNAME | INREASONTEXT | OUTREASONTEXT |
В този грид, основната ми грижа е да определям текста , в стринговите fkCalculated - INREASONTEXT чрез числовото INREASON.
fkCalculated - OUTREASONTEXT чрез числовото OUTREASON
и да записвам САМО числовите полета.
ИЗПОЛЗУВАМ имента на полетата at Dessign Time ( Fiedl Editor).
void __fastcall TJournal::OnMyCalcField(TDataSet* Dataset)
{
switch(INREASON->AsInteger) {
case 1:
INREASONTEXT->AsString=" Идва да товари стока ";
break;
case 2:
INREASONTEXT->AsString=" Носи документ ";
break:
и така нататък
}
switch(OUTREASON->AsInteger) {
case 5:
OUTREASONTEXT->AsString=" Взе стоката и бега... ";
break;
case 6:
INREASONTEXT->AsString=" Взе документите и бега... ";
break:
и така нататък
}
}.
ПРОБЛЕМИ.
По принцип нямам никакви хаотични обръщения към Events, които да правят
щуротии. Опитвах с cached/ "не"chaced updates.
Кода е 5 реда и половина и в него категорично не се вижда грешка.
Сложих едно "наблюдение" във функцията "OnMyCalcField". Оказва се че при
всяка редакция, ( дори и на PDATE ), функцият се разхожда като луда през
всички записи в рекордсета, а не само в текущо редактирвания ред. И да видите
каква интересна картинка !
Представете си, че е избрана една Cell от грида, и след това редактирам
стойността в нея. След редактирането се вижда как през тази цел, като на
екран, се "превъртат" всички стойности на избраното поле (колона), за
целия рекордсет. Нещо, като "прецъкването" на брояча на бензиноколонка.
В същата колона, другите Cell не "мигват".
Иначе казано, ако имам примерно 2 fkCalculated полета, и 100 записа,
броят на обръщенията към OnCalckField e N=2 *100 при режим на
начално зареждане , и N>2 *100 при корекция на КОЕТО и да е поле.
UpdateMode (upWhereAll ), не се оказа причината. ( сякаш ?)
N >= 2 * 100
Има и друго. Скоростта с която работи търсенето:
TLocateOptions tl;
tl.Clear() ;
tl<<loCaseInsensitive;
IBDSEmpPortJournal->Locate("PDATE;EGN,ENTRY", VarArrayOf(.........), tl);
тази скорост просто е абсурдна. Това не ми прилича на търсене по ключ, а филтриране.
Три дена път с камили.
Вчера смених компонента TIBDataSet със TIBClientDataSet. Скоростта на
търсене се увеличи 100-на пъти !!!
Append() е със скоростта на охлюв.Аз си позволявам следното:
IBDSEmpPortJournal->Append();
PDATE->Value=...................;
EGN->Value=..........;
ENTRY->AsInteger=.........;
INREASON->AsInteger=........;
IBDSEmpJournal->ApplyUpdates()
if( !MyTransaction->InTransaction )
MyTransaction->StartTransaction();
MyDataBase->CommitRetaining();
Абе мисля си, че типовете данни нещо не са в ред, TIBDataSet може да е
неподходяща или нещо в първичен ключ. Когато съм пробвал някои
работи с TTable, такива грижи не е имало.
Извинете ако ви надух главата. Тук стана едни майтап.
Имам познат, който преди години е работил с програмиране. Той се ебава така:
"...Иди в музея и ми докарай едно AT 286. Шъ ти напиша код на Clipper, който
работи 1000 пъти по-бързо и N-пъти по надеждно. Ама ще ми туриш и DOS..."
10x.
Хммм...
От това, което си показал не виждам защо се получава този ефект... По принцип не е така. TIBDataSet винаги бих ти го препоръчал пред TIBTable.
По въпроса за Locate-а... Като гледам по колко полета търсиш си мисля, че на тебе ти трябва някой конкретен запис, а не "на тази дата този човек едисикакво си"... Дали няма да е по-добре да си направиш заявка select * from ... where pdate = :pdate and egn = :egn and entry = :entry?
За честото викане на OnCalc... ами виж пропъртито AutoCalcFields. По подразбиране то ти е True и тогава евентът се вика, когато:
- The dataset is opened.
- The dataset is put into dsEdit state.
- Focus moves from one visual control to another, or from one column to another in a data-aware grid and modifications were made to the record.
- A record is retrieved from a database.
Ако го изключиш ще се вика по-рядко. Но не мисля, че това ще ти реши проблема.
Вариант за който се сещам в момента е този код да го прехвърлиш в базата. Ако използваш Firebird 1.5 можеш да го вкараш директно в select-а ти с case. Ако си с по-стар или ползваш Interbase можеш да си направиш една съхранена процедура от сорта на:
и после да си ги вземеш в самия select:
Също така ти каза, че таблицата ти е голяма. В такива случаи е добре да избягваш Lookup и Calculated полета, защото те доста бавят нещата.
Надявам се да съм ти помогнал. Кажи после какво е станало.
Животът не се мери с броя вдишвания, а с моментите, които спират дъха ни!
PS Забравих... Само да добавя, че вместо да ги вкарваш в базата, можеш да се отървеш от calculated полетата и по метода дето каза бат' Кольо с OnGetText
По въпроса за Locate-а... Като гледам по колко полета търсиш си мисля, че на тебе ти трябва някой конкретен запис, а не "на тази дата този човек едисикакво си"... Дали няма да е по-добре да си направиш заявка select * from ... where pdate = :pdate and egn = :egn and entry = :entry?
За честото викане на OnCalc... ами виж пропъртито AutoCalcFields. По подразбиране то ти е True и тогава евентът се вика, когато:
- The dataset is opened.
- The dataset is put into dsEdit state.
- Focus moves from one visual control to another, or from one column to another in a data-aware grid and modifications were made to the record.
- A record is retrieved from a database.
Ако го изключиш ще се вика по-рядко. Но не мисля, че това ще ти реши проблема.
Вариант за който се сещам в момента е този код да го прехвърлиш в базата. Ако използваш Firebird 1.5 можеш да го вкараш директно в select-а ти с case. Ако си с по-стар или ползваш Interbase можеш да си направиш една съхранена процедура от сорта на:
create procedure reasons(inreason smallint, outreason smallint)
returns (inreason_text varchar(50), outreason_text varchar(50))
as
begin
if (inreason = 1) then inreason_text = 'blah 1';
else if (inreason = 2) then ....
if (outreason = 1) then outreason_text = 'blah blah 1';
else if (outreason = 2) then ....
suspend;
end
и после да си ги вземеш в самия select:
select j.pdate, j.egn, j.entry, j.inreason, t.inreason_text, j.outreason, t.outreason_text
from emp_journal j
left join reasons(j.inreason, j.outreason) t on 1=1
where .....
Също така ти каза, че таблицата ти е голяма. В такива случаи е добре да избягваш Lookup и Calculated полета, защото те доста бавят нещата.
Надявам се да съм ти помогнал. Кажи после какво е станало.
Животът не се мери с броя вдишвания, а с моментите, които спират дъха ни!
PS Забравих... Само да добавя, че вместо да ги вкарваш в базата, можеш да се отървеш от calculated полетата и по метода дето каза бат' Кольо с OnGetText
Редактирано от andrew_nikoloff на
Благодаря за съветите, и още нещо.
Благодаря Ви за съветите, особенно на "andrew_nikoloff", защото последвах
съвета му за употребата на процедура. В случая това се оакзва перспективно
за понататък...
В интерес на истината, така и не хванах дивотията с OnCalcField разходките.
Това обаче няма значение вече. Инртесено е другото, по повод на което
пиша тези редове.
Проигравах много вариянти около програмния фрагмент. Сблъсках се със
следния проблем:
Представете си, че имате TIBClientDataSet. Той се генерира от SQL (CommandText)
с най-общ вид:
SELECT J.EGN , J.PDATE, J.REASON ... E.NAME
FROM JOURNAL A , EMPLOYEE E
WHERE J.EGN=E.EGN AND J.PDATE>=:FDATE AND J.PDATE<=:LDATE
Това е един КЛИЕНТСКИ рекордсет, който е multi-table. С него се правят много
удобни неща, особенно при работа със IndexDefs. Много лесно се подреждат данни
според това, къде те стяга чипика, пък и има куп други предимства.
Най лошото е, че когато се опиташ да направиш ApplyUpdates()
само в едната таблица, нещата загрубяват ! Начините хич не са приятни, поне по
това, което предлагат в документацията. Няма ли някакъв "по така" начин за
описване на статични полета, които не се обслуват от DataSetProvider-a.
Моля Ви, ако сте решавали такъв проблем, споделете кой начин според Вас е
добьр.
10x.
съвета му за употребата на процедура. В случая това се оакзва перспективно
за понататък...
В интерес на истината, така и не хванах дивотията с OnCalcField разходките.
Това обаче няма значение вече. Инртесено е другото, по повод на което
пиша тези редове.
Проигравах много вариянти около програмния фрагмент. Сблъсках се със
следния проблем:
Представете си, че имате TIBClientDataSet. Той се генерира от SQL (CommandText)
с най-общ вид:
SELECT J.EGN , J.PDATE, J.REASON ... E.NAME
FROM JOURNAL A , EMPLOYEE E
WHERE J.EGN=E.EGN AND J.PDATE>=:FDATE AND J.PDATE<=:LDATE
Това е един КЛИЕНТСКИ рекордсет, който е multi-table. С него се правят много
удобни неща, особенно при работа със IndexDefs. Много лесно се подреждат данни
според това, къде те стяга чипика, пък и има куп други предимства.
Най лошото е, че когато се опиташ да направиш ApplyUpdates()
само в едната таблица, нещата загрубяват ! Начините хич не са приятни, поне по
това, което предлагат в документацията. Няма ли някакъв "по така" начин за
описване на статични полета, които не се обслуват от DataSetProvider-a.
Моля Ви, ако сте решавали такъв проблем, споделете кой начин според Вас е
добьр.
10x.
Това нямаше ли...
един евент OnApplyUpdates или нещо от сорта?... Говоря малко наизуст, защото във фирмата сме още на делфи 5 про, че ентърпрайса много парички, та нямам ClientDataSet :(
Та идеята ми е, че ако не се лъжа то е като CachedUpdates-ите - вика ти тоя евент за всеки запис и ти казва дали е променен, добавен и т.н. Та евентуално можеш да се закачиш там и да си ъпдейтваш селективно към базата...
Надявам се да не съм те излъгал нещо :)
Животът не се мери с броя вдишвания, а с моментите, които спират дъха ни!
Та идеята ми е, че ако не се лъжа то е като CachedUpdates-ите - вика ти тоя евент за всеки запис и ти казва дали е променен, добавен и т.н. Та евентуално можеш да се закачиш там и да си ъпдейтваш селективно към базата...
Надявам се да не съм те излъгал нещо :)
Животът не се мери с броя вдишвания, а с моментите, които спират дъха ни!