鉴于以下内容
{------------------------------------------------------------------------------}
function TTestClass.NewQuery(const ASql : String) : TSqlQuery;
begin
Result := TSqlQuery.Create(FConn);
Result.SQLConnection := FConn;
Result.Sql.Text := ASql;
Result.Prepared := True;
end;
{------------------------------------------------------------------------------}
procedure TTestClass.ExecuteSql(const ASql : String);
begin
with NewQuery(ASql) do
try
ExecSql();
finally
Free;
end;
end;
如何创建一个填充查询参数的ExecSql
方法?
我尝试了这个重载方法:
{------------------------------------------------------------------------------}
procedure TTestClass.ExecuteSql(const ASql : String; const ParamVals : Array Of Variant);
var
i : integer;
Qry : TSqlQuery;
begin
Qry := NewQuery(ASql);
with Qry do
try
for i := Low(ParamVals) to High(ParamVals) do
Qry.Params[i].Value := ParamVals[i];
ExecSql();
finally
Free;
end;
end;
但我收到错误消息:
Project MyProj.exe引发异常类EDatabaseError,并显示消息'参数没有值'SomeParam''。
观察参数[0]显然显示值已设置 - 参数名称正如我所料。任何人都可以建议我做错了吗?
我过去曾因使用'变异阵列'而受到批评 - 我想知道是否有更好的方法。
谢谢,所有。
我找到了一些有趣的东西:
ParamByName('SomeParam').Value := 1234567;
产生相同的错误消息,而
ParamByName('SomeParam').AsInteger := 1234567;
没有。
我使用DBExpress已经很多年了 - 我忘记了什么吗?
修改
我想出了一种有效的方法,但对此并不满意;通过检查VALUE的变体类型,我已设法获得一些结果
{------------------------------------------------------------------------------}
procedure TMyTestCase.SetParamValues(const AQuery : TSqlQuery; const ParamVals : Array Of Variant);
var
i : Integer;
begin
for i := 0 to AQuery.Params.Count - 1 do begin
case VarType(ParamVals[i]) of
varEmpty : AQuery.Params[i].AsInteger := VarNull; //The variant is Unassigned.
varNull : AQuery.Params[i].AsInteger := VarNull; //The variant is Null.
varAny : AQuery.Params[i].AsInteger := VarNull; //Represents a Variant that can hold any value.
varSmallint : AQuery.Params[i].AsInteger := ParamVals[i]; //16-bit signed integer (type Smallint in Delphi, short in C++).
varInteger : AQuery.Params[i].AsInteger := ParamVals[i]; //32-bit signed integer (type Integer in Delphi, int in C++).
varSingle : AQuery.Params[i].AsFloat := ParamVals[i]; //Single-precision floating-point value (type Single in Delphi, float in C++).
varDouble : AQuery.Params[i].AsFloat := ParamVals[i]; //Double-precision floating-point value (type double).
varCurrency : AQuery.Params[i].AsFloat := ParamVals[i]; //Currency floating-point value (type Currency).
varDate : AQuery.Params[i].AsDateTime := ParamVals[i]; //Date and time value (type TDateTime).
varOleStr : AQuery.Params[i].AsString := ParamVals[i]; //Reference to a dynamically allocated UNICODE string.
varDispatch : AQuery.Params[i].AsInteger := VarNull; //Reference to an Automation object (an IDispatch interface pointer).
varError : AQuery.Params[i].AsInteger := VarNull; //Operating system error code.
varBoolean : AQuery.Params[i].AsBoolean := ParamVals[i]; //16-bit Boolean (type WordBool).
varVariant : AQuery.Params[i].AsInteger := VarNull; //Indicates another variant.
varUnknown : AQuery.Params[i].AsInteger := VarNull; //Reference to an unknown object (an IInterface or IUnknown interface pointer).
varShortInt : AQuery.Params[i].AsInteger := ParamVals[i]; //8-bit signed integer (type ShortInt in Delphi or signed char in C++).
varByte : AQuery.Params[i].AsInteger := ParamVals[i]; //A Byte.
varWord : AQuery.Params[i].AsInteger := ParamVals[i]; //Unsigned 16-bit value (Word).
varLongWord : AQuery.Params[i].AsInteger := ParamVals[i]; //Unsigned 32-bit value (type LongWord in Delphi or unsigned long in C++).
varInt64 : AQuery.Params[i].AsInteger := ParamVals[i]; //64-bit signed integer (Int64 in Delphi or __int64 in C++).
varStrArg : AQuery.Params[i].AsString := ParamVals[i]; //COM-compatible string.
varString : AQuery.Params[i].AsString := ParamVals[i]; //Reference to a dynamically allocated string (not COM-compatible).
varArray : AQuery.Params[i].AsInteger := VarNull; //Indicates a Variant array.
varByRef : AQuery.Params[i].AsInteger := VarNull; //Indicates that the variant contains a reference as opposed to a value.
varTypeMask: AQuery.Params[i].AsInteger := VarNull; //
end;
end;
end;
当然我错过了一步 - 为什么查询参数没有类型?
再次编辑
我目前的“最佳”解决方案是依靠程序员提供正确的类型和数量的值。我已经发布了上面的完整SetParamValues()方法。这是通过NO MEANS彻底测试,但希望它会帮助某人。
答案 0 :(得分:3)
所有使用Delphi XE3和MySQL执行的测试
行为解释
在设计时,您将获得正确的参数数据类型,只要参数取决于FROM表中没有别名的表字段
SELECT id FROM items WHERE id = :id
但如果不是
也会失败SELECT id FROM items WHERE id/2 = :id
这也将失败
SELECT i.* FROM items i WHERE i.id = :id
在运行时,两者都将生成数据类型为ftUnknown
的参数。
参数以私有方法设置
// Delphi XE3
Data.SqlExpr.TCustomSQLDataSet.SetParameterFromSQL
在此方法中,提取的表名仅为
if csDesigning in ComponentState then
使用
创建临时DataSetSELECT * FROM <tablename> WHERE 0 = 1
根据此数据集中的字段名检查每个参数,如果名称匹配,则设置参数数据类型。
这就是你在运行时得到ftUnknown
参数的原因。
要解决Delphi与您尝试使用解决方案相同的问题,但dbexpress有时会失败。 参数的Setter位于
Data.DB.TParam.SetAsVariant
和值1234567
的变体类型为varLongword
,参数数据类型将设置为ftLongword
并导致此错误。
解决方法强>
作为一种解决方法,您可以将参数数据类型设置为ftString / ftWideString,因为这可以在大多数情况下使用。
procedure TTestClass.ExecuteSql(const ASql : String; const ParamVals : Array Of Variant);
var
i : integer;
Qry : TSqlQuery;
begin
Qry := NewQuery(ASql);
with Qry do
try
for i := Low(ParamVals) to High(ParamVals) do
begin
Qry.Params[i].DataType := ftWideString;
Qry.Params[i].Value := ParamVals[i];
end;
ExecSql();
finally
Free;
end;
end;
为了获得更好的解决方案,您将获得一个过程,仅将参数数据类型设置为ftString
/ ftWidestring
,仅用于关键变体类型(如您的方法SetParamValues,但更一般)
procedure SetParamValues( const AParams : TParams; const AValues : array of Variant );
var
LIdx : Integer;
LParam : TParam;
LValue : Variant;
begin
for LIdx := 0 to Pred( AParams.Count ) do
begin
LParam := AParams[LIdx];
LValue := AValues[LIdx];
// only handle the critical parts
case VarType( LValue ) of
varByte, varLongword : LParam.DataType := ftWideString;
end;
// all other will be set here
LParam.Value := LValue;
end;
end;
解决方案:艰难的方式
正如我先说的那样,没有简单的解决办法。对于完整的功能解决方案,您必须解析整个WHERE语句
SELECT a.*, b*
FROM table1 a
JOIN table2 b ON a.id = b.id
WHERE a.id = :id AND b.count / 2 = :halfcount
并从此
构建查询SELECT a.id as Param1, b.count / 2 as Param2
FROM table1 a
JOIN table2 b ON a.id = b.id
WHERE 0 = 1
获取预期的数据类型。
解决方案:漫长道路
恕我直言,这是一个错误,应该向EMBA报告......
解决方案:昂贵的方式
我使用UniDAC进行了测试,所有内容都与dbExpress相同。参数数据类型为ftUnknown
,设置参数值将数据类型设置为ftLongword
。
但有一个特例:您不会收到错误,并且您的查询会按预期处理。