我们已将代码移植到Delphi XE2,并且需要将我们的数据访问组件从不再业务的第三方ODBCExpress更改为dbExpress的TSQLQuery。
我们有参数化的SQL查询,例如:
sSQL :=
'UPDATE ZTestData SET '+
' StringField =?, '+
' IntField = ?, '+
' DecimalField = ?, '+
' BooleanField = ?, '+
' DateTimeField = ?, '+
' TextField = ? '+
' WHERE UniqueID = 3';
如果我们使用以下代码:
var
qry:TSQLQuery;
begin
qry.Close;
qry.SQL.Text := sSQL;
ShowMessage(IntToStr(qry.Params.Count));
end;
它返回0,所以我们无法使绑定工作,但如果我们将sSQL更改为:
sSQL :=
'UPDATE ZTestData SET '+
' StringField =:Param1, '+
' IntField = :Param2, '+
' DecimalField = ?, '+
' BooleanField = ?, '+
' DateTimeField = ?, '+
' TextField = ? '+
' WHERE UniqueID = 3';
它返回2.
将所有SQL查询更改为新参数语法将是一件很麻烦的事。无论如何TSQLQuery都能识别出来吗?语法?
我看到DBXCommon.TDBXCommand使用了?语法:
http://www.andreanolanusse.com/en/parameterized-queries-with-dbexpress-dbx-framework/
但这意味着抛弃使用TSQLQuery的代码。解决此问题的最快捷/最简单的方法是什么?无论如何,TSQLQuery和TDBXCommand之间的区别在于什么与我有关?
答案 0 :(得分:8)
我认为最快的方法是使用将实现此功能的类助手,如:
type
TMyParamsHelper = class Helper for TSQLQuery
public
function SetupParams(AParamList: array of Variant): Boolean; overload;
function SetupParams(ASQL: string; AParamList: array of Variant): Boolean; overload;
end;
// implementation
function TMyParamsHelper.SetupParams(AParamList: array of Variant): Boolean;
var
Index: Integer;
begin
// here you can process the SQL as text and replace each ?
// with :paramINDEX
// first occurence of ? will be :param0, second will be :param1, etc.
// implement your replace algorithm before the "for loop"
for Index := Low(AParamList) to High(AParamList) do
ParamByName(Format(':param%d', [Index])).AsVaraint := AParamList[ Index ];
// of course you need to do it in a try...except block and return TRUE|FALSE
end;
function TMyParamsHelper.SetupParams(ASQL: string; AParamList: array of Variant): Boolean;
begin
SQL.Text := ASQL;
Result := SetupParams( AParamList );
end;
所以现在你所要做的就是打电话:
...
ASQLQueryVariable.SetupParams([2012, 'Hello World', 2.14, 'It WORKS!']);
// or
ASQLQueryVariable.SetupParams(
'UPDATE MyTable SET Year = ?, Title = ?, Cents = ?, Comment = ? WHERE <CLAUSE HERE>',
[2012, 'Hello World', 0.02, 'It WORKS!']
);
...
注意:我正在写这篇文章,可能有拼写错误,可能不是最好的方法......
让我知道这对你有什么影响,我一直想要“?”而不是ParamByName,但实施它太懒了......
答案 1 :(得分:5)
非平凡的方法:
TMyQuery
的TSQLQuery
; TMyQuery
的构造函数中将TStringList(SQL).OnChange
设置为您自己的QueryChanged
方法。有关详细信息,请参阅SqlExpr.pas单元中的TSQLQuery.QueryChanged
。SetParamsFromSQL
调用替换它,它将解析SQL文本并为每个'?'创建参数对象。 occurence。更简单的方法:
TSQLQuery.ParamCheck
设置为False,并在设置SQL
属性后调用proc。最后考虑使用3d派对解决方案,例如AnyDAC。它支持ODBC和'?'参数标记。
答案 2 :(得分:0)
我最终编写了一个方法,将查询中的问号转换为:param1样式参数。有趣的是,Delphi有一个DB.TParams.ParseSQL
方法,可以将参数转换为问号。这种方法基本上与此相反。
function THstmt.AddParamsToSQL(const SQL: String): String;
var
LiteralChar: Char;
CurPos, StartPos, BeginPos: PChar;
ParamCount:Integer;
begin
//Locates the question marks in an SQL statement
//and replaces them with parameters.
//i.e. the reverse of DB.TParams.ParseSQL
//This method is base on DB.TParams.ParseSQL
//For example, given the SQL string
//SELECT * FROM EMPLOYEES WHERE (ID = ?) AND (NAME = ?)
//ParseSQL returns the string
//SELECT * FROM EMPLOYEES WHERE (ID = :1) AND (NAME = :2)
Result := '';
ParamCount := 0;
StartPos := PChar(SQL);
BeginPos := StartPos;
CurPos := StartPos;
while True do
begin
// Fast forward
while True do
begin
case CurPos^ of
#0, '?', '''', '"', '`':
Break;
end;
Inc(CurPos);
end;
case CurPos^ of
#0: // string end
Break;
'''', '"', '`': // literal
begin
LiteralChar := CurPos^;
Inc(CurPos);
// skip literal, escaped literal chars must not be handled because they
// end the string and start a new string immediately.
while (CurPos^ <> #0) and (CurPos^ <> LiteralChar) do
Inc(CurPos);
if CurPos^ = #0 then
Break;
Inc(CurPos);
end;
'?': //parameter
begin
Inc(CurPos);
Inc(ParamCount);
Result := Result + Copy(SQL, StartPos - BeginPos + 1, CurPos - StartPos - 1) + ':' + IntToStr(ParamCount);
StartPos := CurPos;
end;
end;
end;
Result := Result + Copy(SQL, StartPos - BeginPos + 1, CurPos - StartPos);
end;