Question

Je reçois des données à l'aide d'une requête dans Delphi, et je voudrais ajouter un champ calculé à la requête avant d'exécuter. Le champ calculé utilise des valeurs dans le code ainsi que la requête calculer je ne peux pas juste dans SQL.

Je sais que je peux joindre un événement OnCalcFields pour réellement faire le calcul, mais le problème est après l'ajout du champ calculé, il n'y a pas d'autres champs dans la requête ...

Je l'ai fait quelques recherches et a constaté que tous les defs sur le terrain sont créés, mais les champs réels ne sont créés

if DefaultFields then
    CreateFields

Par défaut Les champs est spécifié

procedure TDataSet.DoInternalOpen;
begin
    FDefaultFields := FieldCount = 0;
    ...
end;

Ce qui indiquerait que si vous ajoutez des champs que vous obtenez seulement les champs ajoutés.

Je voudrais que tous les champs de la requête AINSI QUE ceux que j'ajouter.

Est-ce possible ou dois-je ajouter tous les champs que je utilise aussi bien?

Était-ce utile?

La solution

Delphi a maintenant la possibilité de combiner les champs générés automatiquement et les champs calculés: Data.DB.TFieldOptions.AutoCreateMode une énumération de type TFieldsAutoCreationMode . De cette façon, vous pouvez ajouter vos champs calculés lors de l'exécution. François a écrit dans sa réponse comment ajouter un champ à l'exécution.

Différents modes de TFieldsAutoCreationMode:

  • acExclusive

    Quand il n'y a pas de champs persistants à tous, alors les champs automatiques sont créés. Ceci est le mode par défaut.

  • acCombineComputed

    Les champs automatiques sont créés lorsque l'ensemble de données n'a pas de champs persistants ou il n'y a que des champs calculés persistants. Ceci est un moyen pratique pour créer les champs persistants calculés au moment de la conception et de laisser le jeu de données créer des champs de données automatiques.

  • acCombineAlways

    Les champs automatiques pour les champs de base de données seront créés quand il n'y a pas de champs persistants.

Autres conseils

Rien ne vous empêche de créer tous les champs d'abord dans votre code,
puis ajoutez vos champs calculés.

Vous pouvez utiliser un « type piraté » à utiliser les CreateFields protégées:

type
  THackQuery = class(TADOQuery)
  end;
[...]
  MyQuery.FieldDefs.Update;
  THackQuery(MyQuery).CreateFields;

ou emprunter un code 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]);

puis créez vos champs calculés:

  MyQueryMyField := TStringField.Create(MyQuery);
  with MyQueryMyField do
  begin
    Name := 'MyQueryMyField';
    FieldKind := fkCalculated;
    FieldName := 'MyField';
    Size := 10;
    DataSet := MyQuery;
  end;

Vous devez ajouter tous les champs en plus de votre champ calculé.

Une fois que vous ajoutez un champ, vous devez ajouter tous les champs que vous voulez dans l'ensemble de données.

Delphi appelle ces champs persistants par rapport à des champs dynamiques. Tous les champs sont soit persistant ou dynamique. Malheureusement, vous ne pouvez pas avoir un mélange des deux.

Une autre chose à noter, à partir de la documentation est

  

champs persistants listes de composants sont   stockés dans votre application, et ne pas   changer, même si la structure d'un   base de données sous-jacente est un ensemble de données   changé.

Alors, soyez prudent, si vous ajoutez ultérieurement des champs supplémentaires à une table, vous devez ajouter les nouveaux champs au composant. Même chose avec la suppression des champs.

Si vous ne voulez vraiment pas de champs persistants, il y a une autre solution. Sur une grille ou un contrôle qui doit montrer le champ calculé, vous pouvez dessiner sur mesure il. Par exemple, de nombreux contrôles de la grille ont un événement OnCustomDraw. Vous pouvez y faire votre calcul.

Si vous avez connaître vos champs pour être calculés noms lors de l'exécution, vous pouvez utiliser quelque chose comme ça.

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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top