怎么实现如下计算字段?(30分)

  • 主题发起人 主题发起人 xmhong
  • 开始时间 开始时间
X

xmhong

Unregistered / Unconfirmed
GUEST, unregistred user!
想做一计算字段,其值由该记录前几条记录计算得到,
比如前十条记录中的最大值和最小值相除而得。
本人百试不得其解,求助各位大师。
谢谢!
 
You can use triger, :)
 
用TQuery,根据你的实际情况组合SQL语句,它提供搜索最大值、最小值的函数
select max(field1)/min(field2) from table1 where ...
(若想取回返回值可用 变量1:=query1.fields[0].asstring...
然后把变量1再追加回表)
 
用TQuery对本地数据库有限制, local sql中有max, min, avg, sum...出现一定
要指定group by分句, 而且select的列表中不能有非计算字段不在group by 中,
例如:
select code, name, sum(salary) from "demo.db" group by code, name

合法但
select code, name, sum(salary) from "demo.db" group by code
^^^^^^
非法.

而且有这个group by限制, 可能光用一个query不能达到xmhong的要求
 
SQL语句的基本操作对象是集合, 所以您不能指望她可以支持什么
"前十条记录"这样的概念.
您必须在进行查询之后, 自己用程序求出来. (使用游标或者其它方法枚举记录)
 
sample of TTable.OnCalcField:

procedure TForm1.Table1CalcField(DataSet: TDataSet);
var
maxValue, minValue: integer;//or double according to acctural type
StartRec, i, recordNo: integer;
begin
RecordNo:= DataSet.RecNo;
if RecordNo < 10 then StartRec:=0
else StartNo:= RecordNo-10;
maxValue:=DataSet.FieldByName(AName).AsInteger;
minValue:=maxValue;
for i:=StartNo to RecordNo do
begin
if DataSet.FieldByName(AName).AsInteger > maxValue then
maxValue:=DataSet.FieldByName(AName).AsInteger;
if DataSet.FieldByName(AName).AsInteger < minValue then
minValue:=DataSet.FieldByName(AName).AsInteger;
end;
DataSet.FieldByName('MyCalcField').AsDouble:=maxValue/minValue;
end;

be careful of the sort order
 
谢谢各位。
但我还是不大清楚,说明白些好吗?
上面huizhang的程序恐怕不行吧?因为记录指针并未移动,如果加入
RecNo:=...的话,肯定会引起混乱(堆栈溢出).
 
TTable.onfilterrecord(dataset: TDataset; var accept: boolean);
begin
// huaizhang 的程序在这
accept:=true;
end;

只要把ttable.filtered置为true就行了
 
procedure TTable.onfilterrecord(dataset: TDataset; var accept: boolean);
begin
// huaizhang 的程序在这
accept:=true;
end;

只要把ttable.filtered置为true就行了
 
procedure TTable.onfilterrecord(dataset: TDataset; var accept: boolean);
begin
// huaizhang 的程序在这
accept:=true;
end;

只要把ttable.filtered置为true就行了
 
对不起, 我那段程序只是随手写的, 没有经过测试, 缺少定位语句; 不过只是一个思
路, 你自己加上 locate,next 再试一试
 

//以下例程将每十条记录中的某一字段值统计并
//存入记录号模11为零的记录中的计算字段f2中。

var
Form1: TForm1;
Vmax,Vmin: integer; //全局变量:

procedure TForm1.FormShow(Sender: TObject);
begin
Vmax := 0;
Vmin := high( integer );
end;

procedure TForm1.Table1CalcFields(DataSet: TDataSet);
var
Vcur: integer;
begin
Vcur := DataSet.FieldByName('EmpNo').AsInteger;
if (DataSet.RecNo Mod 11) = 0 then
begin
DataSet.FieldByName('f2').AsInteger := Vmax div Vmin;
Vmax := 0;
Vmin := high( integer );
end;
if Vcur > Vmax then Vmax := Vcur;
if Vcur < Vmin then Vmin := Vcur;
end;
 
与我的想法差很多。
但好象有点快浮出水面的感觉......
 
不是所有的数据库都支持RecNo
 
//以下例程将每相邻的十条记录的某一字段的最大值/最小值
//存入当前记录中的计算字段f2中。如果不想包含每相邻十条
//记录中的最后一条记录的该字段值,则将求最大值最小值的
//循环次数减一就可以了。同理,修改Vn的值就可以得到每相
//邻Vn条记录的计算结果了。


//全局变量:
Const
Vn = 10;

var
Form1: TForm1;
Varray: array[0..Vn-1] of integer;
Vpointer: integer;

procedure TForm1.FormShow(Sender: TObject);
var
i: integer;
begin
for i:=0 to Vn-1 do Varray := 0;
end;

procedure TForm1.Table1CalcFields(DataSet: TDataSet);
var
i,j: integer;
begin
Varray[Vpointer] := DataSet.FieldByName('EmpNo').AsInteger;
j := Vpointer-1;
if j < 0 then j := Vn - 1;
inc(Vpointer);
Vpointer := Vpointer Mod Vn;
Vmax := 0;
Vmin := high( integer );
for i:=0 to Vn-1 do
begin
if Varray[j] > Vmax then Vmax := Varray[j];
if Varray[j] < Vmin then Vmin := Varray[j];
inc(j);
j := j mod Vn;
end;
DataSet.FieldByName('f2').AsInteger := Vmax div Vmin;
end;
 
附注: 若当前记录该字段值肯定要参加计算,

则循环中的j均用i代替,并去掉以下处理j的

语句:

j := Vpointer-1;
if j < 0 then j := Vn - 1;

。。。

inc(j);
j := j mod Vn;

就可以了
 
有一个苯方法:
-----再建立一个查询
 
在BEFORE INSERT中保存需要计算的字段的值,在ON计算事件中进行计算不得了
 
我想关键问题不是怎么求出上面或下面几个记录和的问题,
而是在计算字段中如果改变当前的记录,就会再次触发计算字段事件
造成递归导致堆栈溢出.
解决的办法是,进入你的计算字段时间之后,关闭你的计算字段事件,
关闭之后,可以随便的改变数据表的记录也不会调用你的计算字段事件函数.
在结束时,在恢复计算字段事件就行了.

事件函数可以这样写:
procedure TForm1.Table1CalcFields(DataSet: TDataSet);
begin
Table1.onCalcFields := nil; //关闭计算事件
....
....
Table1.onCalcFields := Table1CalcFields; //恢复事件
end;
 
好注意
在查数据时最好用另一个TABLE/QUERY
 
后退
顶部