2

我正在使用本地 FireDAC Postgres 驱动程序使用从 FireDAC 到 PostgreSQL 11 的命名参数执行查询。在准备语句期间,FireDAC 将命名参数转换为位置参数,这是正确的。但是,如果我随后尝试为这些参数赋值,FireDAC 会引发“参数超出范围”异常。FireDAC 似乎无法识别它生成的位置参数。例如,如果原始 SQL 文本如下所示:

SELECT * FROM account WHERE accountid = :accid;

在调用 FDQuery 的 Prepare 方法后,FireDAC 将此查询转换为:

SELECT * FROM account WHERE accountid = $1;

但是当我尝试将值分配给参数时,我得到了错误。分配看起来像这样:

FDQuery1.Params[0].AsString = strID;

其中 strID 是一个字符串值,accountid 是一个文本字段。此外,如果我使用类似以下的内容,它会返回 0。

ShowMessage( IntToStr( FDQuery1.Params.Count ) );

我已经大大简化了这段代码,但问题是一样的。如何让 FireDAC 识别它生成的位置参数?


更新:正如我所提到的,上面的代码被大大简化了。实际发生的是,在我们的框架中,我们有一组例程为 FireDAC 宏分配值,然后我们通过准备查询然后读取 FDQuery 的 Text 属性来生成 SQL 语句。然后,该 SQL 语句被分配给另一个 FDQuery(也是动态创建的)的 SQL.Text 属性,并且在那里查询失败。所以,这里有一个非常简单的例子,说明代码内部发生了什么:

var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create( nil );
  Query.Connection := PGConnection;
  // In reality, the SQL statement below was generated earlier,
  // from a function call where the SQL was created by the FireDAC
  // SQL preprocessor, as opposed to being a literal expression
  Query.SQL.Text := 'SELECT * FROM tablename WHERE field2 = $1;';
  Query.Params[0].AsString := '4'; // BANG! Argument out of range

我以为可能是FireDAC宏扩展的原因,所以在实例化FDQuery后添加了以下两行:

Query.ResourceOptions.MacroCreate := False;
Query.ResourceOptions.MacroExpand := False;

没有。这也有帮助。我猜 FireDAC 根本不承认 $1 是 PostgreSQL 中的有效位置参数。

4

2 回答 2

1

您需要将符号参数标识符(在您的情况下:accid)放在 SQL.Text 字符串中,而不是位置代码($1

我刚刚测试了这两种变体。第一个有效,第二个无效。

var
  MyQ : tfdquery;
begin
  MyQ := Tfdquery.Create(nil);
  MyQ.Connection := dm1.dbMain;
  MyQ.SQL.Text := 'Select * from person where lastname = :lname;';
  MyQ.Params[0].AsString := 'Brodzinsky';
  MyQ.Open();
  ShowMessage('Records found = '+MyQ.RecordCount.ToString);
  MyQ.Close;
  MyQ.Free;
end;

接下来尝试使用位置。当然,FireDac没有看到冒号,所以不知道有没有参数要创建

var
  MyQ : tfdquery;
begin
  MyQ := Tfdquery.Create(nil);
  MyQ.Connection := dm1.dbMain;
  MyQ.SQL.Text := 'Select * from person where lastname = $1;';
  MyQ.Params[0].AsString := 'Brodzinsky';
  MyQ.Open();
  ShowMessage('Records found = '+MyQ.RecordCount.ToString);
  MyQ.Close;
  MyQ.Free;
end;

首先,person 表中的 11 条记录与我的姓氏一起返回;在第二种情况下,会生成 Argument Out Of Range 错误,因为 SQL 文本中没有指定参数。注意:我正在访问一个 MySQL 数据库,但这里的问题是 Delphi 和 FireDac 预处理代码以发送到数据库服务器。

于 2019-07-10T15:08:05.467 回答
1

好的,可能有一种方法可以使用 FireDAC 属性解决此问题,但我还没有找到它。但是,对于这种特殊情况,在一种方法中准备 SQL,然后从另一种方法中分配给不同的 FDQuery,我找到了答案。由于 PostgreSQL 允许命名参数使用数字,例如 :1,我所做的就是将 $ 字符替换为 : 字符。如在

var
  SQLStmt: String;
  FDQuery: TFDQuery;
begin
  SQLStmt :=  'SELECT * FROM tablename WHERE field2 = $1;';
  FDQuery := TFDQuery.Create( nil );
  FDQuery.Connection := FDConnection1;
  FDQuery.SQL.Text := SQLStmt.Replace( '$', ':' );
  FDQuery.Params[0].AsString := 'SomeValue'); // Works!
  ...

而且,是的,如果您的查询包含多个命名参数的实例,FireDAC 会将其替换为相同的数字。

如果我找到另一个解决方案,我会发布它。如果其他人提出使用 FireDAC 属性的解决方案,我将选择该答案作为正确答案。

于 2019-07-11T20:55:51.070 回答