如何使用 FireDAC 中的匿名代码块在 PostgreSQL 中执行带有参数的函数?

How to execute function with parameter in PostgreSQL using anonymous code block in FireDAC?

我的数据库 (PostgreSQL 11) 有一个必须调用才能执行操作的函数。 return 不重要,只要没有错误。此函数具有必须作为参数传递的参数。 我不能直接使用 TFDConnection.ExecSQL 因为我使用了该方法不支持的数组类型的参数(据我所知)。所以,我这样使用 TFDQuery.ExecSQL

msql1: = 'DO $$ BEGIN PERFORM DoIt (:id,:items); END $$; ';
    
{... create FDQuery (fq), set connection, set SQL.Text to msql1}
      
fq.Params.ParamByName ('id'). AsInteger: = 1;
    
{$ REGION 'items'}
fq.ParamByName ('items'). DataType: = ftArray;
fq.ParamByName ('items'). ArrayType: = atTable; // Must be atTable, not atArray
if length (items)> 0 then
begin
  fq.ParamByName ('items'). ArraySize: = length (items);
  for it: = 0 to length (items) -1 do
  fq.ParamByName ('items'). AsIntegers [it]: = items [it];
end;
fq.ExecSQL;
{$ ENDREGION}

执行上面的代码时,出现上面的错误消息

"Parameter 'id' not found".

经过一些研究建议使用 fq.Params.ParamByName 我也没有成功。

但是,如果您将调用函数的方式更改为 select DoIt (:id,:items); 并且显然用 fq.Open 替换执行非常有效。 是否可以使用 TFDConnection / TFDQuery 执行包含此块调用的函数中的参数的 PL / pgSQL 块?

PS:我正在使用 Delphi Rio 10.3.3

当您为 TFDQuery.SQL 赋值时,FireDAC 会根据各种选项对 SQL 进行一些预处理。 ResourceOptions.CreateParams 选项控制是否应处理参数。这是默认启用的。

预处理器识别您的 SQL 中的字符串文字,并且不会尝试在其中查找参数。您使用了 dollar quoted string constant,这就是 FireDAC 无法识别其中参数的原因。即使您手动添加参数,我认为 FireDAC 也不会绑定该值。

话虽如此,执行存储 procedures/function 的正确方法是使用 TFDStoredProc。您只需分配 StoredProcName 并调用其 Prepare 方法,该方法将从数据库中检索过程的元数据(参数),因此您无需设置参数的 ArrayTypeDataType

在您的代码中,您将 DataType 设置为 ftArray 这是错误的,因为在数组参数的情况下,它应该设置为数组的元素类型。无论如何,通过设置 fq.ParamByName ('items').AsIntegers,您有效地将参数的 DataType 设置为 ftInteger。您需要做的就是设置 ArraySize

您应该改为这样做:

procedure DoIt(Connection: TFDConnection; ID: Integer; const Items: TArray<Integer>);
var
  StoredProc: TFDStoredProc;
  ParamItems: TFDParam;
  Index: Integer;
begin
  StoredProc := TFDStoredProc.Create(nil);
  try
    StoredProc.Connection := Connection;
    StoredProc.StoredProcName := 'DoIt';
    StoredProc.Prepare;
    StoredProc.Params.ParamByName('id').AsInteger := ID;
    if Length(Items) > 0 then
    begin
      ParamItems := StoredProc.Params.ParamByName('items');
      ParamItems.ArraySize := Length(Items);
      for Index := Low(Items) to High(Items) do
        ParamItems.AsIntegers[Index] := Items[Index];
    end;
    StoredProc.ExecProc;
  finally
    StoredProc.Free;
  end;
end;

或者您可以使用 ExecFunc 来获取存储函数的结果。