Delphi 如果还有日期时间字段,10.4 无法在 DBGrids 上显示十进制 (5,2) BCD 字段
Delphi 10.4 fails to show decimal(5,2) BCD fields on DBGrids if there also is a datetime field
当我有一个带有日期时间字段和小数 (5,2) 字段的 ClientDataset 时,Delphi 10.4 无法在 TDBGrid 上显示它们,它会引发转换异常。
我已经准备了一个小测试项目来显示这个错误(我的真实数据来自 SQL 服务器,尽管我可以手动填充客户端数据集得到相同的错误)。
procedure TForm1.FormCreate(Sender: TObject);
var ClientDataset: TClientDataset;
Datasource: TDatasource;
DBGrid: TDBGrid;
begin
ClientDataset := TClientDataset.Create(Self);
ClientDataset.FieldDefs.Add('Id', ftInteger);
ClientDataset.FieldDefs.Add('Date', ftDateTime);
ClientDataset.FieldDefs.Add('Decimal', ftBCD, 2);
ClientDataset.FieldDefs.Find('Decimal').Precision := 5;
ClientDataset.CreateDataSet;
Datasource := TDatasource.Create(Self);
Datasource.Dataset := ClientDataset;
ClientDataset.Insert;
ClientDataset.FieldValues['id'] := 1;
ClientDataset.FieldValues['Date'] := Now;
ClientDataset.FieldValues['Decimal'] := 7.55;
ClientDataset.Post;
ClientDataset.Insert;
ClientDataset.FieldValues['id'] := 2;
ClientDataset.FieldValues['Date'] := Now;
ClientDataset.FieldValues['Decimal'] := 8.2;
ClientDataset.Post;
DBGrid := TDBGrid.Create(Self);
DBGrid.Parent := Self;
DBGrid.Align := alClient;
DBGrid.Datasource := Datasource;
end;
它引发了这个异常:'8200@' is not a valid integer value
decimal(18,2) 没有这个问题,如果我注释行 ClientDataset.FieldDefs.Find('Decimal').Precision := 5;
则不会引发错误。
此外,如果没有日期时间字段,则也不会引发错误。这运行良好:
procedure TForm1.FormCreate(Sender: TObject);
var ClientDataset: TClientDataset;
Datasource: TDatasource;
DBGrid: TDBGrid;
begin
ClientDataset := TClientDataset.Create(Self);
ClientDataset.FieldDefs.Add('Id', ftInteger);
ClientDataset.FieldDefs.Add('Decimal', ftBCD, 2);
ClientDataset.FieldDefs.Find('Decimal').Precision := 5;
ClientDataset.CreateDataSet;
Datasource := TDatasource.Create(Self);
Datasource.Dataset := ClientDataset;
ClientDataset.Insert;
ClientDataset.FieldValues['id'] := 1;
ClientDataset.FieldValues['Decimal'] := 7.55;
ClientDataset.Post;
ClientDataset.Insert;
ClientDataset.FieldValues['id'] := 2;
ClientDataset.FieldValues['Decimal'] := 8.2;
ClientDataset.Post;
DBGrid := TDBGrid.Create(Self);
DBGrid.Parent := Self;
DBGrid.Align := alClient;
DBGrid.Datasource := Datasource;
end;
您认为无需将我的所有小数 (5,2) 字段替换为小数 (18,2) 字段即可解决此问题吗?
更新: 这是 ClientDataset 特有的问题。如果我在 TADOQuery 或 TFDMemTable 上打开相同的数据(具有完全相同的日期时间和十进制(5,2)BCD 字段),Delphi 10.4 显示 DBGrid 没有任何问题。
问题也是 DBGrid 特有的。在 DBEdits 上显示这些字段没有问题。
单元 Data.FmtBcd 的版本 10.4 中已对函数 BCDToCurr 和 BCDToCurrency 进行了更改。
Delphi10.3
function BCDToCurr(const BCD: TBcd; var Curr: Currency): Boolean;
Curr := StrToCurr(string(Bcd));
Result := True;
end;
function BCDToCurrency(const BCD: TBcd): Currency;
begin
Result := StrToCurr(string(Bcd));
end;
Delphi10.4
function BCDToCurr(const BCD: TBcd; var Curr: Currency): Boolean;
var
B: TBcd;
S: string;
const
DecimalSeparator = '.';
begin
// B := BCD * 10000;
B := BCD;
if BcdScale(B) >= 4 then
Dec(B.SignSpecialPlaces, 4)
else if BcdPrecision(B) <= (MaxFMTBcdFractionSize - 4) then
Inc(B.Precision, 4)
else
OverflowError(SBcdOverflow);
S := BcdToStr(B, DecimalSeparator);
Round(S, DecimalSeparator, 0);
// The real format of Currency type is Int64.
PInt64(@Curr)^ := StrToInt64(S);
Result := True;
end;
function BCDToCurrency(const BCD: TBcd): Currency;
begin
BCDToCurr(BCD, Result);
end;
因此,在 BCDToCurr 函数中精度为:=5:
否则如果 BcdPrecision(B) <= (MaxFMTBcdFractionSize - 4) 那么
公司(B.Precision, 4)
和 B.Precision 递增到 9。然后变量 Curr 取了一个不正确的值。
我报告了这个错误,现在已在最近的 Delphi 10.4 更新 1 中修复。
当我有一个带有日期时间字段和小数 (5,2) 字段的 ClientDataset 时,Delphi 10.4 无法在 TDBGrid 上显示它们,它会引发转换异常。
我已经准备了一个小测试项目来显示这个错误(我的真实数据来自 SQL 服务器,尽管我可以手动填充客户端数据集得到相同的错误)。
procedure TForm1.FormCreate(Sender: TObject);
var ClientDataset: TClientDataset;
Datasource: TDatasource;
DBGrid: TDBGrid;
begin
ClientDataset := TClientDataset.Create(Self);
ClientDataset.FieldDefs.Add('Id', ftInteger);
ClientDataset.FieldDefs.Add('Date', ftDateTime);
ClientDataset.FieldDefs.Add('Decimal', ftBCD, 2);
ClientDataset.FieldDefs.Find('Decimal').Precision := 5;
ClientDataset.CreateDataSet;
Datasource := TDatasource.Create(Self);
Datasource.Dataset := ClientDataset;
ClientDataset.Insert;
ClientDataset.FieldValues['id'] := 1;
ClientDataset.FieldValues['Date'] := Now;
ClientDataset.FieldValues['Decimal'] := 7.55;
ClientDataset.Post;
ClientDataset.Insert;
ClientDataset.FieldValues['id'] := 2;
ClientDataset.FieldValues['Date'] := Now;
ClientDataset.FieldValues['Decimal'] := 8.2;
ClientDataset.Post;
DBGrid := TDBGrid.Create(Self);
DBGrid.Parent := Self;
DBGrid.Align := alClient;
DBGrid.Datasource := Datasource;
end;
它引发了这个异常:'8200@' is not a valid integer value
decimal(18,2) 没有这个问题,如果我注释行 ClientDataset.FieldDefs.Find('Decimal').Precision := 5;
则不会引发错误。
此外,如果没有日期时间字段,则也不会引发错误。这运行良好:
procedure TForm1.FormCreate(Sender: TObject);
var ClientDataset: TClientDataset;
Datasource: TDatasource;
DBGrid: TDBGrid;
begin
ClientDataset := TClientDataset.Create(Self);
ClientDataset.FieldDefs.Add('Id', ftInteger);
ClientDataset.FieldDefs.Add('Decimal', ftBCD, 2);
ClientDataset.FieldDefs.Find('Decimal').Precision := 5;
ClientDataset.CreateDataSet;
Datasource := TDatasource.Create(Self);
Datasource.Dataset := ClientDataset;
ClientDataset.Insert;
ClientDataset.FieldValues['id'] := 1;
ClientDataset.FieldValues['Decimal'] := 7.55;
ClientDataset.Post;
ClientDataset.Insert;
ClientDataset.FieldValues['id'] := 2;
ClientDataset.FieldValues['Decimal'] := 8.2;
ClientDataset.Post;
DBGrid := TDBGrid.Create(Self);
DBGrid.Parent := Self;
DBGrid.Align := alClient;
DBGrid.Datasource := Datasource;
end;
您认为无需将我的所有小数 (5,2) 字段替换为小数 (18,2) 字段即可解决此问题吗?
更新: 这是 ClientDataset 特有的问题。如果我在 TADOQuery 或 TFDMemTable 上打开相同的数据(具有完全相同的日期时间和十进制(5,2)BCD 字段),Delphi 10.4 显示 DBGrid 没有任何问题。
问题也是 DBGrid 特有的。在 DBEdits 上显示这些字段没有问题。
单元 Data.FmtBcd 的版本 10.4 中已对函数 BCDToCurr 和 BCDToCurrency 进行了更改。
Delphi10.3
function BCDToCurr(const BCD: TBcd; var Curr: Currency): Boolean;
Curr := StrToCurr(string(Bcd));
Result := True;
end;
function BCDToCurrency(const BCD: TBcd): Currency;
begin
Result := StrToCurr(string(Bcd));
end;
Delphi10.4
function BCDToCurr(const BCD: TBcd; var Curr: Currency): Boolean;
var
B: TBcd;
S: string;
const
DecimalSeparator = '.';
begin
// B := BCD * 10000;
B := BCD;
if BcdScale(B) >= 4 then
Dec(B.SignSpecialPlaces, 4)
else if BcdPrecision(B) <= (MaxFMTBcdFractionSize - 4) then
Inc(B.Precision, 4)
else
OverflowError(SBcdOverflow);
S := BcdToStr(B, DecimalSeparator);
Round(S, DecimalSeparator, 0);
// The real format of Currency type is Int64.
PInt64(@Curr)^ := StrToInt64(S);
Result := True;
end;
function BCDToCurrency(const BCD: TBcd): Currency;
begin
BCDToCurr(BCD, Result);
end;
因此,在 BCDToCurr 函数中精度为:=5:
否则如果 BcdPrecision(B) <= (MaxFMTBcdFractionSize - 4) 那么 公司(B.Precision, 4)
和 B.Precision 递增到 9。然后变量 Curr 取了一个不正确的值。
我报告了这个错误,现在已在最近的 Delphi 10.4 更新 1 中修复。