如何使用 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
方法,该方法将从数据库中检索过程的元数据(参数),因此您无需设置参数的 ArrayType
或 DataType
。
在您的代码中,您将 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
来获取存储函数的结果。
我的数据库 (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
方法,该方法将从数据库中检索过程的元数据(参数),因此您无需设置参数的 ArrayType
或 DataType
。
在您的代码中,您将 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
来获取存储函数的结果。