Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   paso de parametros a tdataset (https://www.clubdelphi.com/foros/showthread.php?t=89754)

Kenobi 31-01-2016 13:20:07

paso de parametros a tdataset
 
Hola, frecuentemente utilizo una rutina como esta para "enlazar" un dataset con otro
Código Delphi [-]
if not consulta.prepared then consulta.prepare;
consulta.close;
consulta.parambyname('parametro').asLoqueSea := valor
consulta.open;

pues bien, estoy intentando crear un procedure generico que reciba 2 parametros uno el dataset y el otro el campo:

Código Delphi [-]
procedure generico(data:tdataset;campo:String)
begin
data.paramByName(campo).asvariant := campo;

el problema es que el dta set no me acepta el paramByName e intentado con :

Código Delphi [-]
tquery(data).parambyname(campo).asVAriant := campo

pero no resulta...

espero que se entienda la idea
Gracias

Casimiro Notevi 31-01-2016 13:40:08

No puedes pasar el nombre de un campo como parámetro.

Además, creo recordar que está mal el planteamiento que haces aquí:
Código Delphi [-]
if not consulta.prepared then 
begin
  consulta.prepare;
  consulta.close;
  consulta.parambyname('parametro').asLoqueSea := valor;
  consulta.open;
end;
Creo recordar que, en todo caso, aunque no es necesario el prepare, debes hacer así:
Código Delphi [-]
consulta.close;
consulta.parambyname('parametro').asLoqueSea := valor;
consulta.prepare;
consulta.open;

Lepe 31-01-2016 14:39:58

La idea del "prepare", es que se cree el SQL con parámetros (como ya lo haces), y se compila una sola vez la instrucción (prepare), se da valor a los parámetros y se ejecuta/abre.

Tiene sentido cuando un mismo SQL se va a ejecutar varias veces en la misma tabla con los mismos campos, por ejemplo inserciones masivas, actualizaciones, selecciones, etc...

Código SQL [-]
select * from tabla where fecha between :fecha_inicial and :fecha_final

Muy típico en listados por fechas, creas la ventana de listados donde asignas el SQL, lo preparas y cuando el usuario cambia las fechas en la interfaz, solo asignas el valor de los parámetros.

En teoría es lo más eficiente, ya que evitas:
- averiguar los campos que se seleccionan (solo se hace una vez en la vida del Form, donde se crean los campos del TQuery, rellenando el FieldsDef, etc).
- compilar el SQL para obtener el plan de ejecución (qué índices se usarán según los join que hagas, los campos del order by, etc)

Es cierto que no es necesario llamar a "prepare" porque lo hace internamente, pero es buena práctica; solo con ver un "if not query.prepared", sabes que el SQL no se modifica. Obviamente no tiene sentido hacerlo en un FormCreate, porque si se acaba de crear el Form y el TQuery, aún no está preparado.

Saludos

Kenobi 31-01-2016 17:39:34

Cita:

Empezado por Casimiro Notevi (Mensaje 501725)
No puedes pasar el nombre de un campo como parámetro.

Hola, creo que si se puede de hecho lo estoy haciendo en este momento, claro con algo mas en el código:

tengo 2 tquery

personas:
Código SQL [-]
SELECT * from personas WHERE campo=:dNI

autos:
Código SQL [-]
SELECT * from autos WHERE campo=:matricula


Código Delphi [-]
procedure cargar_query(nombreParametro,valorParametro:String;
begin
  query.close;
  query.paramByName(nombreParametro).asVariant := valorParametro;
  query.open;
end;

y luego para llamar al procedure pues :
Código Delphi [-]
cargar_query('DNI','007');  // ....aca me carga al agente ....
cargar_query('matricula','bmw-007'); // ....  y su auto ....

a lo que me refiero es que le puedo pasar el nombre y el valor de un parametro al paramByName ya que lo que espera es un String ....

claro que es lo que falta pues como decir con un parametro cual es la query en cuestion, lo intente con

Código Delphi [-]
procedure cargar_query(query:TdataSet;nombreParametro,valorParametro:String);
begin
  query.close;
  query.paramByName(nombreParametro).asVariant := valorParametro;  // un tdataset no acepta el metodo paramByname ....
  query.open;
end;

mi idea es hacer algo asi

Código Delphi [-]
 tquery(query).paramByName(nombreParametro).asVariant:=valorParametro;

, pero no funciona

y alli esta el detalle ....lo que quiero es hacer ese casting y eliminar muchas declaraciones de este procedure para cada query en particular y asi reduciria mucho el codigo ...

Saludos ...

AgustinOrtu 31-01-2016 18:31:28

Muy complicado de hacer; las diferentes implementaciones estan en distintos arboles de herencia.

Por ejemplo en DB.pas se define el tipo TParam para operar con los parametros de los TQuery

Pero en ADODB.pas se define otro tipo TParameter que no tiene nada que ver con TParam

Tampco hay un ancestro comun para los TQuery. ADOQuery hereda de TCustomAdoDataSet quien hereda de TDataSet; y es en TCustomAdoDataSet donde se agrega la funcionalidad para los TParameter


Lo mas cercano que podes hacer es declarar una interface y luego crear clases wrapper, asi:

Código Delphi [-]
unit Unit3;

interface

type
  IQuery = interface
    ['{055FBC2E-FB1C-41DC-9696-B3071C8D04C4}']
    procedure SetParamValue(const AName: string; AValue: Variant);
    function GetParamValue(const AName: string): Variant;
    procedure Close;
    procedure Open;
  end;

procedure cargar_query(const AQuery: IQuery; const Parametro, Valor: string);

implementation

procedure cargar_query(const AQuery: IQuery; const Parametro, Valor: string);
begin
  AQuery.Close;
  AQuery.SetParamValue(Parametro, Valor);
  AQuery.Open;
end;

end.

Wrapper ADO:

Código Delphi [-]
type
  TAdoQueryWrapper = class(TADOQuery, IQuery)
  public
    procedure SetParamValue(const AName: string; AValue: Variant);
    function GetParamValue(const AName: string): Variant;
  end;

{ TAdoQueryWrapper }

function TAdoQueryWrapper.GetParamValue(const AName: string): Variant;
begin
  Result := Parameters.ParamByName(AName).Value;
end;

procedure TAdoQueryWrapper.SetParamValue(const AName: string; AValue: Variant);
begin
  Parameters.ParamByName(AName).Value := AValue;
end;

Ejemplo de uso:

Código Delphi [-]
procedure TForm2.Button1Click(Sender: TObject);
var
  AQuery: TAdoQueryWrapper;
begin
  AQuery := TAdoQueryWrapper.Create(NIL);
  AQuery.Connection := ADOConnection1;
  DataSource1.DataSet := AQuery;
  AQuery.SQL.Text := ' SELECT * FROM Clientes WHERE Id > :Id ';
  cargar_query(AQuery, 'Id', '5');
end;

fjcg02 01-02-2016 09:35:45

Es cierto lo que comentan, dependiendo del dataset, hay diferentes formas de definir los parámetros de un dataset.

Dataset.ParamByName() ..

ó

DataSet.Parameters.ParamByName()...

Tendrás que hacer unas pruebas, y ver si utilizas una forma u otra teniendo en cuenta qué objetos utilizas. Si utilizas diferentes objetos, tendrás que hacer un cast.

Código SQL [-]
if dataset.Class = TQUery then
  Dataset.ParamByName( ....

if dataset.Class = TADOQuery then
  Dataset.Parameters.ParamByName(....

Dataset.Open;
Espero haberte dado alguna idea.

Un saludo

AgustinOrtu 01-02-2016 14:25:43

No es buena idea, ya que si cambias la clase del dataset, te tenes que acordar de agregar el cast a esa clase; es agregar un if

Ademas, si se manda por parametro un TDataSet, eso quiere decir que puedo mandar sin problemas un TTable, un TClientDataSet; y evidentemente eso no es lo que espera el procedimiento

Tambien implica agregar en la clausula uses las distintas bibliotecas: ADODB para TADOxx, DB para TDataSet, DBClient para TClientDataSet; esto es malo porque es acoplamiento (se depende de otra unidad para poder compilar la nuestra). El codigo acoplado es mas dificil de mantener, mas dificil de testear, mas dificil de aislar. Yo siempre estoy luchando con sacar todo lo que sobra en los uses, y si puedo poner todo en la seccion de implementation, mejor aun

Delphi por suerte soporta OOP e interfaces, no volvamos a la programacion estructurada

Lo mas sano es declarar una interface y enviarla como parametro; si mas adelante cambio de TADOQuery a TQuery, al enviarlo por parametro el compilador me va a recordar amablemente que la clase TQuery no implementa dicha interface

Hay que aprender a escribir codigo que arroje los errores en tiempo de compilacion, no en ejecucion


La franja horaria es GMT +2. Ahora son las 09:46:21.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi