为什么大富翁就没人能回答这问题,我等到花儿也谢了[:(] 怎样在DBGrid中达到某个字段的当前记录(行)的值等于以上所有记录(行)的合计值。 (100分)

  • 主题发起人 主题发起人 zheng
  • 开始时间 开始时间
Z

zheng

Unregistered / Unconfirmed
GUEST, unregistred user!
例:
记录号 当月值 此前合计
1 50 50
2 100 150
3 20 170
4....
最好能用计算字段。
 
添加统计字段或者利用sql语句
 
我用的是ado连接和adoquery,双击adoquery1,然后add new field .在adoquery1中加一计算字段取名为mysum,然后在adoquery
的oncalcfields事件中:
procedure TForm1.ADOQuery1CalcFields(DataSet: TDataSet);
var i:integer;
myqr:tadoquery;
begin
try
myqr := tadoquery.Create(nil);
myqr.Connection := ADOConnection1;
i := dataset.fieldbyname('纪录号').AsInteger;
myqr.SQL.Text := 'select sum(当月值) as num from zz where 纪录号<='+inttostr(i);
myqr.Open;
mysum.Value := myqr.fieldbyname('num').AsInteger;
finally
freeandnil(myqr);
end;
end;
 
最糟的是我的表是用"UNION"出的临时表!
 
如果你的序号唯一标示一行的话,我觉得用两个ADODataSet,一个用来统计,它只需要
行号和所需统计的列就可以了,并在其打开后将统计的结果放到一个可以用行号标示的
数组。另一个增加一个计算字段。注意,统计的ADODataSet,要先打开。我试了一下,
应该可以的。我用ADOConnection,ADODataSet,连接Sql Sever 的NorthWind 数据库的
[Sales Totals by Amount]表。原文件如下:
:pas
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, DBCtrls, Grids, DBGrids, Db, ADODB;

type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
ADODataSet1: TADODataSet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
Button1: TButton;
ADODataSet1SaleAmount: TBCDField;
ADODataSet1OrderID: TAutoIncField;
ADODataSet1CompanyName: TWideStringField;
ADODataSet1ShippedDate: TDateTimeField;
ADODataSet1cl_sum: TFloatField;
ADODataSet2: TADODataSet;
procedure Button1Click(Sender: TObject);
procedure ADODataSet1CalcFields(DataSet: TDataSet);
procedure ADODataSet2AfterOpen(DataSet: TDataSet);
private
{ Private declarations }
arrSum: array of Real;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
ADODataSet2.Close;
ADODataSet2.Open;
ADODataSet1.Close;
ADODataSet1.Open;
end;

procedure TForm1.ADODataSet1CalcFields(DataSet: TDataSet);
var
i: integer;
begin
with ADODataSet1 do
begin
i := FieldValues['OrderID'];
FieldValues['cl_sum'] := arrSum;
end;
end;

procedure TForm1.ADODataSet2AfterOpen(DataSet: TDataSet);
var
i: integer;
_Sum: Real;
begin
Setlength(arrSum,100000);
_Sum := 0;
with ADODataSet2 do
begin
First;
while not Eof do
begin
_Sum := _Sum + FieldValues['SaleAmount'];
i := FieldValues['OrderID'];
arrSum := _Sum;
Next;
end;
end;
end;

end.

:dfm

object Form1: TForm1
Left = 192
Top = 107
Width = 696
Height = 480
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object DBGrid1: TDBGrid
Left = 0
Top = 96
Width = 688
Height = 357
Align = alBottom
DataSource = DataSource1
ImeName = '中文 (简体) - 微软拼音'
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'MS Sans Serif'
TitleFont.Style = []
Columns = <
item
Expanded = False
FieldName = 'OrderID'
Visible = True
end
item
Expanded = False
FieldName = 'SaleAmount'
Visible = True
end
item
Expanded = False
FieldName = 'CompanyName'
Visible = True
end
item
Expanded = False
FieldName = 'ShippedDate'
Visible = True
end
item
Expanded = False
FieldName = 'cl_sum'
Width = 160
Visible = True
end>
end
object DBNavigator1: TDBNavigator
Left = 202
Top = 68
Width = 240
Height = 25
DataSource = DataSource1
TabOrder = 1
end
object Button1: TButton
Left = 352
Top = 32
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 2
OnClick = Button1Click
end
object ADOConnection1: TADOConnection
Connected = True
ConnectionString =
'Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security In' +
'fo=False;Initial Catalog=Northwind;Data Source=chs;Use Procedure' +
' for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ' +
'ID=CHS'
LoginPrompt = False
Provider = 'SQLOLEDB.1'
Left = 62
Top = 52
end
object ADODataSet1: TADODataSet
Connection = ADOConnection1
CursorType = ctStatic
OnCalcFields = ADODataSet1CalcFields
CommandText = 'select * from [Sales Totals by Amount]'
Parameters = <>
Left = 102
Top = 52
object ADODataSet1SaleAmount: TBCDField
FieldName = 'SaleAmount'
Precision = 19
end
object ADODataSet1OrderID: TAutoIncField
FieldName = 'OrderID'
ReadOnly = True
end
object ADODataSet1CompanyName: TWideStringField
FieldName = 'CompanyName'
Size = 40
end
object ADODataSet1ShippedDate: TDateTimeField
FieldName = 'ShippedDate'
end
object ADODataSet1cl_sum: TFloatField
FieldKind = fkCalculated
FieldName = 'cl_sum'
Calculated = True
end
end
object DataSource1: TDataSource
DataSet = ADODataSet1
Left = 154
Top = 54
end
object ADODataSet2: TADODataSet
Connection = ADOConnection1
AfterOpen = ADODataSet2AfterOpen
CommandText = 'select * from [Sales Totals by Amount]'
Parameters = <>
Left = 156
Top = 20
end
end

 
最簡單的方法是用Tclientdaset作顯示的臨時表,建立你要的字段,把adoquery中的數據一邊轉一邊計算.
幾分鐘就搞定了.
 
理解能力太低,我,能说明白一点吗?这个问题应该不难
 
例:
记录号 当月值 此前合计
1 50 50
2 100 150
3 20 170
4....
明白
select 当月值,此前合计 from a
union
select sum(当月值),sum(此前合计) from a
 
我使用的方法,但有缺陷,请大家帮我改正:
procedure TKcSpMxForm.WareStoresDetailCalcFields(DataSet: TDataSet);
begin
//当拉住滚动条不放,上下移动,最终放下的位置与起始位一致时,就出错。
with WareStoresDetail do
begin
if (RecNo = -1) then
FBackwordCalcField := not FBackwordCalcField;
if FBackwordCalcField then //按正常方向计算“计算字段”,即从头到尾。
begin
Balance := Balance + (FieldByName('InNumber').asInteger - FieldByName('OutNumber').asInteger);
FieldByName('Balance').Value := Balance; //库存余量
{FMoneyBalance := FMoneyBalance + (FieldByName('InNumber').asInteger + FieldByName('OutNumber').asInteger)*SpKcForm.WareStoresPrice.asInteger;
FieldByName('MoneyBalance').Value := FMoneyBalance; //库存余额}
end else
begin
FieldByName('Balance').Value := Balance;
Balance := Balance - (FieldByName('InNumber').asInteger - FieldByName('OutNumber').asInteger);
end;
end;
end;
 
其实,这个很简单,真的。
你每修改一个记录时,运行一个过程,这个过程的思路如下:
1.修改前,把这个记录的旧值记录下来
2.将表中所有大于该记录号的合计数减除记录下来的旧值
3.将表中所有大于该记录号的合计数加上记录的新值

注意,还要处理新增记录和删除记录。
肯定行。干吧,就看你会不会写这样的代码了(杞人忧天!)。
 
呵 全你还是换控件吧 , 一句代码也不用写
比如说 DbGridEh 或 ExpressQuantumGrid都不错
 
ramble:
你有没有看清楚,上面的"此前合计"的值怎么得来?
你说说DbGridEh哪有这样的功能 !
 
其实有很多控件即稳定有有源码, 功能够可靠, 完全可以把控件(或源码)直接拿过来用
, 不过要多试 , 会使你受益匪浅
 
个人觉得应该很简单!用个全局变量在CalcFields事件中每次叠加,再写回计算字段!

没试验过!
 
我这有一专门计算这类问题的例子:如需请与我联系!hzq_526@sohu.com
 
说这么清楚还不会做,真是没救了![:D]
 
上面这些人都没有仔细看,如果那么容易,我也不用问啦,我现在只能用
TClientDataSet生成临时表,但效率较低。
好像tan_jian说但:
“用个全局变量在CalcFields事件中每次叠加,再写回计算字段!”就是没看我的代码,
我原来不就是这样写吗,但你看一看就知道,有问题:
“当拉住滚动条不放,上下移动,最终放下的位置与起始位一致时,就出错”
因为此时“RecNo”值不能正确判断。



 
给你代码:你只要写一个计算的式子带入就行:
代码:
unit strafununit;
interface
uses dialogs,
sysutils;
type
TdouarrAy = array of double; //声明数值栈类型
Toperset = set of char;//声明操作符集合
{数值栈和操作符栈的入栈函数}
{Soper为当前的操作符栈,Coper为要压入的操作符}
function OperPush(var Soper:string;Coper:char):boolean;overload;
{Sdata为当前的数值栈,Cdata为要压入的数值}
function OperPush(var Sdata:TdouarrAy;Cdata:double):boolean;overload;
{数值栈和操作符栈的出栈函数,其它同上}
function OperPop(var Soper:string;var Coper:char):boolean;overload;
function OperPop(var Sdata:TdouarrAy;var Cdata:double):boolean;overload;
{运算判断函数,fdata为前面的数值,moper为中间的操作符,
bdata为后面的数值、返回运算后的数值}
function OperDiag(fdata:double;moper:char;bdata:double):double;
{数值栈和操作符栈的得到栈顶元素的函数}
function GetTopOper(Soper:string):char;overload;
function GetTopOper(Sdata:TdouarrAy):double;overload;
{操作符优先级比较函数,Foper为前面的操作符,Boper为后面的操作符}
function PriComp(Foper:char;Boper:char):char;
{从输入字符串中截取数值或操作符函数}
function GetData(var Toper:string):string;
{操作符字符串解析主函数}
function StrAnalysis( var Toper:string):double;
implementation
{操作符栈入栈出栈函数}
function OperPush(var Soper:string;Coper:char):boolean;
begin
soper:=soper+coper;//当前操作符压入操作符栈顶
result:=true;
end;
function OperPop(var Soper:string;var Coper:char):boolean;
var slen:integer;
begin
slen:=length(Soper);
Coper:=Soper[slen];//弹出操作符栈顶操作符
Soper:=copy(Soper,1,slen-1);
result:=true;
end;
{数值栈入栈出栈函数}
function OperPush(var Sdata:TdouarrAy;Cdata:double):boolean;
var dlen:integer;
begin
dlen:=length(Sdata);
setlength(Sdata,dlen+1);
Sdata[dlen]:=Cdata;//当前数值入栈
result:=true;
end;
function OperPop(var Sdata:TdouarrAy;var Cdata:double):boolean;
var dlen:integer;
begin
dlen:=length(Sdata);
if dlen-1<0 then exit;
Cdata:=sdata[dlen-1];//当前数值出栈
Sdata:=copy(Sdata,0,dlen-1);//栈顶下移
result:=true;
end;
{运算判断函数,fdata为前面的数值,moper为中间的操作符,
bdata为后面的数值、返回运算后的数值}
function OperDiag(fdata:double;moper:char;bdata:double):double;
begin
case moper of
'+':result:=bdata+fdata;
'-':result:=bdata-fdata;
'*':result:=bdata*fdata;
'/':try
result:=bdata/fdata;
except
showmessage('错误!除数不能为零');
result:=0; //除数为零时的异常处理
end;
else begin
showmessage('错误!无此操作符');
result:=0;
end;
end;
end;
{操作符栈的得到栈顶元素的函数}
function GetTopOper(Soper:string):char;
var slen:integer;
begin
slen:=length(Soper);
result:=Soper[slen];// 得到栈顶操作符
end;
{数值栈得到栈顶元素的函数}
function GetTopOper(Sdata:TdouarrAy):double;
var slen:integer;
begin
slen:=length(Sdata);
result:=Sdata[slen-1];//得到栈顶数值
end;
{操作符优先级比较函数,Foper为前面的操作符,Boper为后面的操作符}
function PriComp(Foper,Boper:char):char;
var coper:toperset;
begin
Coper:=['+','-','*','/','#','(',')'];
{操作符优先级比较}
if (Foper in coper)and(Boper in coper) then
begin
case foper of
'+':begin
{'+'优先级低于 '*','/','(' }
if (Boper='*')or(Boper='/')or(Boper='(')then result:='<'
else result:='>';
end;
{'-'优先级低于 '*','/','(' }
'-':begin
if (Boper='*')or(Boper='/')or(Boper='(')then result:='<'
else result:='>';
end;
{'*'优先级低于 '(' }
'*':begin
if Boper='('then result:='<'
else result:='>';
end;
{'/'优先级低于 '(' }
'/':begin
if Boper='('then result:='<'
else result:='>';
end;
{ '('碰到 ')' 则输出去括号标志‘=’}
'(':begin
if Boper=')'then result:='='
else result:='<';
end;
{')'优先级最高}
')':result:='>';
{‘#’为字符串开始与结束标志位}
'#':begin
if Boper='#'then result:='='
else result:='<';
end;
else begin
showmessage('错误,字符串输入有误!');
result:='x';
end;
end;
end
else begin
showmessage('错误,字符串中有操作符以外的字符!');
result:='x';
end;
end;
{从输入字符串中截取数值或操作符函数}
function getdata(var Toper:string):string;
var
operset:Toperset;
tstr:string;
i,TempLength:integer;
begin
i:=1;
tstr:='';
operset:=['+','-','*','/','(',')','#'];//操作符集
{如果是操作符则截取操作符}
if Toper[1] in operset then
begin
result:=Toper[1];
TempLength:=length(Toper);
Toper:=copy(Toper,2,TempLength-1);
{如果是数值则截取数值}
end
else
while not(Toper in operset) do
begin
tstr:=tstr+Toper;
i:=i+1;
end;
if tstr<>'' then begin
result:=tstr;
Toper:=copy(Toper,length(tstr)+1,length(Toper)-length(tstr));
//字符串中截去取出的部分
end;
end;
{操作符字符串解析主函数}
function StrAnalysis(var Toper:string):double;
var operset:Toperset;//操作符集
sdata:TdouarrAy;//数值栈
soper:string;//操作符栈
getstr:string;//存放从字符串中截取操作符和数值的临时变量
theta:char;// 存放操作符的临时变量
doudataf:double;//存放数值的临时变量
doudatar:double;//存放数值的临时变量
i:integer;
begin
{初始化栈及数据}
setlength(sdata,0);//初始化数值栈
soper:='';//初始化操作符栈
operset:=['+','-','*','/','(',')','#']; //定义操作符集
doudataf:=0;
doudatar:=0;
theta:=' ';
Toper:='#'+Toper+'#';//给字符串加上开始和结束标志符
//先处理有负数的情况
for i:=2 to length(Toper) do
begin
if (Toper='-') and (Toper[i-1] in operset) and (Toper[i-1]<>')') then
begin
Toper:='~';//表示正负的负号用’~‘代替
end;
end;
{因为OperPush函数后面的参数为char型所以写成GetData(Toper)[1]}
OperPush(soper,GetData(Toper)[1]);//先把‘#’入栈
getstr:=GetData(Toper);//得到 ‘#’ 后的第一个数值或操作符
{‘#’与‘#’未碰到既字符串未处理完}
while(getstr<>'#')or(GetTopOper(soper)<>'#') do
begin
{因为operset是char集所以写成getstr[1}
if not(getstr[1] in operset) then//如果不是操作符则如数值栈
begin
if getstr[1]='~' then getstr[1]:='-';
OperPush(sdata,strtofloat(getstr));
getstr:=GetData(Toper);//取下一数值或操作符
end
else// 如果是操作符则与操作符栈顶元素先比较优先级
case pricomp(GetTopOper(soper),getstr[1]) of
'<': begin //如果是操作符栈顶元素优先级低则直接入操作符栈
OperPush(soper,getstr[1]);
getstr:=GetData(Toper);//取下一数值或操作符
end;
'=':begin//如果是’=‘则脱括号
OperPop(soper,theta);
getstr:=GetData(Toper);//取下一数值或操作符
end;
'>':begin //如果是操作符栈顶元素优先级高则先计算,不用取下一数值或操作符
OperPop(soper,theta);
OperPop(sdata,doudataf);
OperPop(sdata,doudatar);
OperPush(sdata,OperDiag(doudataf,theta,doudatar));
//运算结果入栈
end;
end;
end;
result:=GetTopOper(sdata);//返回结果既数值栈的栈顶元素
end;
end.
 
后退
顶部