¿Cómo ejecutar función con parámetro en PostgreSQL utilizando bloqueo de código anónimo en FireDAC?

Mi base de datos (PostgreSQL 11) tiene una función que debe ser llamada a ejecutar una acción. El regreso no es importante, siempre y cuando no haya error. Esta función tiene argumentos que deben pasarse como parámetros. No puedo utilizarlo directamente TFDConnection.ExecSQL porque uso parámetros de tipo array que no es compatible con el método (a mi conocimiento). Así que, yo uso TFDQuery.ExecSQL así:

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}

Al ejecutar el código anterior, el error anterior aumenta

"Parameter 'id' no encontrado".

Después de una investigación que sugirió usar fq.Params.ParamByName También fui infructuosa.

Sin embargo, si cambia la forma en que se llama a la función select DoIt (:id,:items); y obviamente reemplazando la ejecución por fq.Open funciona perfectamente. ¿Es posible ejecutar un bloque PL / pgSQL que contiene parámetros en la función llamada por este bloque utilizando TFDConnection / TFDQuery?

PS: Estoy usando Delphi Rio 10.3.3

Pregunta hecha hace 3 años, 5 meses, 0 días - Por techtrailblazer


3 Respuestas:

  • Cuando asignas un valor a TFDQuery.SQL, FireDAC hace algún preprocesamiento del SQL basado en varias opciones. ResourceOptions.CreateParams Opción controla si los parámetros deben ser procesados. Esto está habilitado por defecto.

    El preprocesador reconoce literales de cadena en su SQL y no trata de buscar los parámetros en ellos. Tú usaste. dólar cotizado cadena constante y por eso FireDAC no reconoce los parámetros. Incluso si agregas un parámetro manualmente creo que FireDAC no ataría el valor.

    Con eso dicho, la manera adecuada de ejecutar procedimientos/función almacenados es utilizar TFDStoredProc. Acabas de asignar StoredProcName y llamar a Prepare método que recuperará los metadatos del procedimiento (parameters) de la base de datos para que no necesite establecer ArrayType o DataType del parámetro.

    En su código usted establece el DataType a ftArray lo que es incorrecto, porque en caso de parámetro array debe establecerse para el tipo de elemento de array. De todos modos, estableciendo fq.ParamByName ('items').AsIntegers Usted efectivamente establece el parámetro DataType a ftInteger. Todo lo que necesitas hacer es establecer ArraySize

    Esto es lo que deberías hacer en su lugar:

    procedure DoIt(Connection: TFDConnection; ID: Integer; const Items: TArray);
    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;
    

    Alternativamente puede utilizar ExecFunc para obtener el resultado de la función almacenada.

    Respondida el Dec 17, 2020 a las 19:17 - por pixelprodigy

    Votos positivos: 0 | Votos negativos: 0

  • Para ejecutar una función con parámetro en PostgreSQL utilizando bloqueo de código anónimo en FireDAC, puedes intentar lo siguiente:

    En lugar de utilizar fq.Params.ParamByName para establecer los parámetros, intenta asignar directamente los valores a través de TFDQuery.Params de la siguiente manera:

    delphi
    msql1 := 'DO $$ BEGIN PERFORM DoIt(:id,:items); END $$;';
    
    {... create FDQuery (fq), set connection, set SQL.Text to msql1}
    
    fq.Params[0].AsInteger := 1; // Establecer el parámetro id
    
    {$REGION 'items'}
    fq.Params[1].DataType := ftArray;
    fq.Params[1].ArrayType := atTable; // Debe ser atTable, no atArray
    if Length(items) > 0 then
    begin
      fq.Params[1].ArraySize := Length(items);
      for it := 0 to Length(items) - 1 do
        fq.Params[1].AsIntegers[it] := items[it];
    end;
    fq.ExecSQL;
    {$ENDREGION}
    

    En este código, he reemplazado fq.ParamByName con fq.Params[], donde el índice 0 corresponde al parámetro id y el índice 1 corresponde al parámetro items.

    Espero que esta solución te ayude a resolver el error "Parameter 'id' not found" al ejecutar la función con parámetros en PostgreSQL utilizando FireDAC en Delphi. ¡Déjame saber si tienes alguna otra pregunta o si necesitas más ayuda!

    Respondida el Dec 18, 2020 a las 19:14 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Solución:

    El problema es que FireDAC no admite directamente la asignación de nombres de parámetros a bloques de código PL/pgSQL. Sin embargo, hay una solución alternativa que puedes utilizar:

    • Crea un procedimiento almacenado que llame a la función con parámetros nombrados.
    • Utiliza `TFDConnection.ExecProc` para ejecutar el procedimiento almacenado, pasando los parámetros necesarios.

    Ejemplo:

    Procedimiento almacenado:

    sql
    CREATE OR REPLACE PROCEDURE DoItWithParams(id integer, items integer[])
    AS $$
    BEGIN
        PERFORM DoIt(id, items);
    END;
    $$ LANGUAGE plpgsql;
    

    Código FireDAC:

    delphi
    // Crea un FDConnection y establece la conexión a la base de datos
    msql1 := 'CALL DoItWithParams(:id, :items);';
    {... Crea FDQuery (fq), establece la conexión y establece SQL.Text en msql1}
    
    fq.Params.ParamByName('id').AsInteger := 1;
    
    {$ REGION 'items'}
    fq.ParamByName('items').DataType := ftArray;
    fq.ParamByName('items').ArrayType := atTable;
    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.ExecProc;
    {$ ENDREGION}
    

    Beneficios:

    Esta solución proporciona un enfoque más limpio y estructurado para ejecutar funciones con parámetros en PostgreSQL utilizando FireDAC. Permite utilizar nombres de parámetros, lo que mejora la legibilidad y el mantenimiento del código. * Aprovecha las funciones nativas de PostgreSQL, lo que puede mejorar el rendimiento en algunos casos.

    Respondida el Dec 18, 2020 a las 19:19 - por Gemini

    Votos positivos: 0 | Votos negativos: 0