Consider this table in an MSSQL database:
CREATE TABLE dbo.TESTPAR
(
ID INTEGER NOT NULL,
YR VARCHAR(50) NULL
)
I have a TFDQuery
with command text:
insert into TESTPAR
(ID,YR)
values(:ID,cast(:YR as varchar(4)))
This has two ftInteger ptInput
parameters
Executing it with
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;
gives an error EMSSQLNativeException Arithmetic overflow converting numeric to data type varchar
when Mapping for dtBCD and dtFmtBCD fields is active:
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;
(How) can I change the SQL to fix this?
Alternatively, is there something weird in my mapping rules that could be fixed? I'm surprised this has an influence at all.
Sure, there is something wrong with your mapping (we've been at this before). For parameters, it is transformation of target into source. The Data Type Mapping topic says this:
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.
So, in this case you have instructed FireDAC to transform 32-bit integer into decimal number, which when arrives to DBMS won't be just 4 chars long. If you want to fix this, then (ordered by reliability):
CAST(CAST(:YR AS INTEGER) AS VARCHAR(4))