Adición de un campo calculado a una consulta en tiempo de ejecución
-
22-09-2019 - |
Pregunta
Estoy recibiendo datos mediante una consulta en Delphi, y me gustaría añadir un campo calculado a la consulta antes de que se ejecute. El campo calculado está utilizando valores de código, así como la consulta así que no puedo calcularlo en SQL.
Sé que puedo adjuntar un evento OnCalcFields
para hacer realidad el cálculo, pero el problema es después de agregar el campo calculado no hay otros campos en la consulta ...
Hice algo de investigación y encontró que todos los defs de campo son creados pero los campos reales sólo se crean
if DefaultFields then
CreateFields
Los campos por defecto se especifica
procedure TDataSet.DoInternalOpen;
begin
FDefaultFields := FieldCount = 0;
...
end;
lo que indicaría que si agrega campos que sólo recibe los campos que agregó.
Me gustaría que todos los campos de la consulta, así como el que más me Agregar.
¿Es esto posible o tengo que añadir todos los campos que estoy usando así?
Solución
Delphi tiene ahora la opción de combinar campos generados automáticas y campos calculados: Data.DB.TFieldOptions.AutoCreateMode una enumeración de tipo TFieldsAutoCreationMode . De esta manera usted puede añadir sus campos calculados en tiempo de ejecución. Francois escribió en su respuesta cómo agregar un campo en tiempo de ejecución.
Los diferentes modos de TFieldsAutoCreationMode:
-
acExclusive
Cuando no hay campos persistentes en absoluto, entonces se crean campos automáticos. Este es el modo por defecto.
-
acCombineComputed
Los campos automáticos se crean cuando el conjunto de datos no tiene campos persistentes o hay solamente se calculan campos persistentes. Esta es una manera conveniente de crear los campos calculados persistentes en tiempo de diseño y dejar que el conjunto de datos a crear campos de datos automáticos.
-
acCombineAlways
campos automáticos para los campos de la base se crean cuando no hay campos persistentes.
Otros consejos
Nada le impide la creación de todos los campos primero en el código, España a continuación, añadir sus campos calculados.
Se puede utilizar un "Tipo de piratería", para usar las CreateFields protegidas:
type
THackQuery = class(TADOQuery)
end;
[...]
MyQuery.FieldDefs.Update;
THackQuery(MyQuery).CreateFields;
o tomar prestado algo de código de CreateFields:
MyQuery.FieldDefs.Update;
// create all defaults fields
for I := 0 to MyQuery.FieldDefList.Count - 1 do
with MyQuery.FieldDefList[I] do
if (DataType <> ftUnknown) and not (DataType in ObjectFieldTypes) and
not ((faHiddenCol in Attributes) and not MyQuery.FIeldDefs.HiddenFields) then
CreateField(Self, nil, MyQuery.FieldDefList.Strings[I]);
a continuación, crear sus campos calculados:
MyQueryMyField := TStringField.Create(MyQuery);
with MyQueryMyField do
begin
Name := 'MyQueryMyField';
FieldKind := fkCalculated;
FieldName := 'MyField';
Size := 10;
DataSet := MyQuery;
end;
Es necesario añadir todos los campos, además de su campo calculado.
Una vez que se agrega un campo, hay que añadir todos los campos que desee en el conjunto de datos.
Delphi llama a esto campos persistentes frente a campos dinámicos. Todos los campos son o persistente o dinámico. Por desgracia, no se puede tener una mezcla de ambos.
Otra cosa a la nota, de la documentación es
campos persistentes listas de componentes son almacenado en la aplicación, y no hacer el cambio incluso si la estructura de una base de datos subyacente es un conjunto de datos cambiado.
Por lo tanto, tenga cuidado, si más adelante añadir campos adicionales a una tabla, tendrá que añadir los nuevos campos para el componente. Lo mismo con la supresión de los campos.
Si realmente no quiere campos persistentes, no hay otra solución. En cualquier red o de control que debe mostrar el campo calculado, se puede dibujar a medida que. Por ejemplo, muchos controles cuadrícula tienen un evento OnCustomDraw. Usted puede hacer sus cálculos allí.
Si ha de ser conocer sus nombres campos calculados en tiempo de ejecución, puede utilizar algo por el estilo.
var
initing:boolean;
procedure TSampleForm.dsSampleAfterOpen(
DataSet: TDataSet);
var
i:integer;
dmp:tfield;
begin
if not initing then
try
initing:=true;
dataset.active:=false;
dataset.FieldDefs.Update;
for i:=0 to dataset.FieldDefs.Count-1 do
begin
dmp:=DataSet.FieldDefs.Items[i].FieldClass.Create(self);
dmp.FieldName:=DataSet.FieldDefs.Items[i].DisplayName;
dmp.DataSet:=dataset;
if (dmp.fieldname='txtState') or (dmp.FieldName='txtOldState') then
begin
dmp.Calculated:=true;
dmp.DisplayWidth:=255;
dmp.size:=255;
end;
end;
dataset.active:=true;
finally
initing:=false;
end;
end;
procedure TSampleForm.dsSampleAfterClose(
DataSet: TDataSet);
var
i:integer;
dmp:TField;
begin
if not initing then
begin
for i:=DataSet.FieldCount-1 downto 0 do
begin
dmp:=pointer(DataSet.Fields.Fields[i]);
DataSet.Fields.Fields[i].DataSet:=nil;
freeandnil(dmp);
end;
DataSet.FieldDefs.Clear;
end;
end;
procedure TSampleForm.dsSampleCalcFields(
DataSet: TDataSet);
var
tmpdurum,tmpOldDurum:integer;
begin
if not initing then
begin
tmpDurum := dataset.FieldByName( 'state' ).AsInteger;
tmpOldDurum:= dataset.FieldByName( 'oldstate' ).AsInteger;
dataset.FieldByName( 'txtState' ).AsString := State2Text(tmpDurum);
dataset.FieldByName( 'txtOldState' ).AsString := State2Text(tmpOldDurum);
end;
end;
procedure TSampleForm.btnOpenClick(Sender: TObject);
begin
if dsSample.Active then
dsSample.Close;
dsSample.SQL.text:='select id,state,oldstate,"" as txtState,"" as txtOldState from states where active=1';
dsSample.Open;
end;