当 BCD 映射规则处于活动状态时,在 INSERT 查询中转换参数值会导致算术溢出
Casting a parameter value in INSERT query gives arithmetic overflow when BCD mapping rules are active
在 MSSQL 数据库中考虑这个 table:
CREATE TABLE dbo.TESTPAR
(
ID INTEGER NOT NULL,
YR VARCHAR(50) NULL
)
我有一个 TFDQuery
命令文本:
insert into TESTPAR
(ID,YR)
values(:ID,cast(:YR as varchar(4)))
这有两个 ftInteger ptInput
参数
用
执行
procedure TFrmCastAsVarchar.BtnTestInsertClick(Sender: TObject);
begin
Inc(FLastID);
FDQuery2.Params[0].AsInteger := FLastID;
FDQuery2.Params[1].AsInteger := 2018;
try
FDQuery2.ExecSQL;
except
on E:Exception do ShowMessage(E.Message);
end;
end;
当 dtBCD 和 dtFmtBCD 字段的映射处于活动状态时出现错误 EMSSQLNativeException Arithmetic overflow converting numeric to data type varchar
:
procedure TDM.SetBCDMapRules;
// For (Fmt)BCD data types. Called from SetOracleMapRules/SetMSSQLMapRules
begin
with FDConnection.FormatOptions.MapRules.Add do
begin // Convert numeric data types with scale=0 and precision<=10 to a 32-bit integer
PrecMax := 10;
PrecMin := 0;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtBCD;
TargetDataType := dtInt32;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Do the same for those that might return as dtFmtBCD instead of dtBCD
PrecMax := 10;
PrecMin := 0;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtFmtBCD;
TargetDataType := dtInt32;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Convert numeric data types with scale=0 and precision>10 to a 64-bit integer
PrecMin := 11;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtBCD;
TargetDataType := dtInt64;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Idem dtFmtBCD
PrecMin := 11;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtFmtBCD;
TargetDataType := dtInt64;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // All other dtBCD types (notably scale <> 0) should return as float
SourceDataType := dtBCD;
TargetDataType := dtDouble;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Idem dtFmtBCD
SourceDataType := dtFmtBCD;
TargetDataType := dtDouble;
end;
end;
(如何)我可以更改 SQL 来解决这个问题?
或者,我的映射规则中有什么奇怪的东西可以修复吗?我很惊讶这有什么影响。
- 这当然只是一个基本的例子。真正的脚本将其他字符串连接到 cast() 以得出要放入 varchar 字段的 varchar 值。
- 不 使用 BCD 映射会产生其他问题(例如,使用 DECIMAL 字段类型)。
- 正在为客户端更改 table 结构 "is not optimal" ;-)
- 我已经使用许多不同的 ODBC/native 驱动程序对此进行了测试。
- 这是 Delphi Tokyo 10.2.3,Win7 上的 Win32 应用程序。
当然,您的映射有问题(我们一直在这个 ). For parameters, it is transformation of target into source. The Data Type Mapping 主题中说:
In case of a command parameter, the rule defines a transformation of a
target data type, specified by an application, into a source data
type, supported by a driver.
因此,在这种情况下,您已指示 FireDAC 将 32 位整数转换为十进制数,当到达 DBMS 时,它不会只有 4 个字符长。如果你想解决这个问题,那么(按可靠性排序):
- 在 table
中使用正确的数据类型
- 一般停止使用映射规则
- 使用正确的参数数据类型并按实际情况传递值(以字符串形式,而非整数形式)
- 将参数值转换为整数,例如
CAST(CAST(:YR AS INTEGER) AS VARCHAR(4))
在 MSSQL 数据库中考虑这个 table:
CREATE TABLE dbo.TESTPAR
(
ID INTEGER NOT NULL,
YR VARCHAR(50) NULL
)
我有一个 TFDQuery
命令文本:
insert into TESTPAR
(ID,YR)
values(:ID,cast(:YR as varchar(4)))
这有两个 ftInteger ptInput
参数
用
执行procedure TFrmCastAsVarchar.BtnTestInsertClick(Sender: TObject);
begin
Inc(FLastID);
FDQuery2.Params[0].AsInteger := FLastID;
FDQuery2.Params[1].AsInteger := 2018;
try
FDQuery2.ExecSQL;
except
on E:Exception do ShowMessage(E.Message);
end;
end;
当 dtBCD 和 dtFmtBCD 字段的映射处于活动状态时出现错误 EMSSQLNativeException Arithmetic overflow converting numeric to data type varchar
:
procedure TDM.SetBCDMapRules;
// For (Fmt)BCD data types. Called from SetOracleMapRules/SetMSSQLMapRules
begin
with FDConnection.FormatOptions.MapRules.Add do
begin // Convert numeric data types with scale=0 and precision<=10 to a 32-bit integer
PrecMax := 10;
PrecMin := 0;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtBCD;
TargetDataType := dtInt32;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Do the same for those that might return as dtFmtBCD instead of dtBCD
PrecMax := 10;
PrecMin := 0;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtFmtBCD;
TargetDataType := dtInt32;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Convert numeric data types with scale=0 and precision>10 to a 64-bit integer
PrecMin := 11;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtBCD;
TargetDataType := dtInt64;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Idem dtFmtBCD
PrecMin := 11;
ScaleMax := 0;
ScaleMin := 0;
SourceDataType := dtFmtBCD;
TargetDataType := dtInt64;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // All other dtBCD types (notably scale <> 0) should return as float
SourceDataType := dtBCD;
TargetDataType := dtDouble;
end;
with FDConnection.FormatOptions.MapRules.Add do
begin // Idem dtFmtBCD
SourceDataType := dtFmtBCD;
TargetDataType := dtDouble;
end;
end;
(如何)我可以更改 SQL 来解决这个问题?
或者,我的映射规则中有什么奇怪的东西可以修复吗?我很惊讶这有什么影响。
- 这当然只是一个基本的例子。真正的脚本将其他字符串连接到 cast() 以得出要放入 varchar 字段的 varchar 值。
- 不 使用 BCD 映射会产生其他问题(例如,使用 DECIMAL 字段类型)。
- 正在为客户端更改 table 结构 "is not optimal" ;-)
- 我已经使用许多不同的 ODBC/native 驱动程序对此进行了测试。
- 这是 Delphi Tokyo 10.2.3,Win7 上的 Win32 应用程序。
当然,您的映射有问题(我们一直在这个
In case of a command parameter, the rule defines a transformation of a target data type, specified by an application, into a source data type, supported by a driver.
因此,在这种情况下,您已指示 FireDAC 将 32 位整数转换为十进制数,当到达 DBMS 时,它不会只有 4 个字符长。如果你想解决这个问题,那么(按可靠性排序):
- 在 table 中使用正确的数据类型
- 一般停止使用映射规则
- 使用正确的参数数据类型并按实际情况传递值(以字符串形式,而非整数形式)
- 将参数值转换为整数,例如
CAST(CAST(:YR AS INTEGER) AS VARCHAR(4))