実行時に計算フィールドをクエリに追加する
-
22-09-2019 - |
質問
Delphi のクエリを使用してデータを取得していますが、実行前に計算フィールドをクエリに追加したいと考えています。計算フィールドはクエリだけでなくコード内の値も使用しているため、SQL で単純に計算することはできません。
添付できることはわかっています OnCalcFields
実際に計算を行うイベントですが、問題は、計算フィールドを追加した後、クエリに他のフィールドが存在しないことです...
少し調べてみたところ、フィールド定義はすべて作成されているが、実際のフィールドは作成されているだけであることがわかりました。
if DefaultFields then
CreateFields
デフォルトのフィールドが指定されています
procedure TDataSet.DoInternalOpen;
begin
FDefaultFields := FieldCount = 0;
...
end;
これは、フィールドを追加すると、追加したフィールドのみが取得されることを示します。
クエリ内のすべてのフィールドと、追加したフィールドも含めたいと思います。
これは可能ですか、それとも使用しているすべてのフィールドも追加する必要がありますか?
解決
Delphi には、自動生成フィールドと計算フィールドを組み合わせるオプションが追加されました。 Data.DB.TFieldOptions.AutoCreateMode 型の列挙 TFieldsAutoCreationMode. 。このようにして、実行時に計算フィールドを追加できます。Francois は、実行時にフィールドを追加する方法を答えに書きました。
TFieldsAutoCreationMode のさまざまなモード:
ac限定
永続フィールドがまったくない場合は、自動フィールドが作成されます。これがデフォルトのモードです。
acCombine計算済み
自動フィールドは、データセットに永続フィールドがない場合、または計算された永続フィールドのみがある場合に作成されます。これは、設計時に永続的な計算フィールドを作成し、データセットに自動データ フィールドを作成させる便利な方法です。
acCombineAlways
データベースフィールドの自動フィールドは、永続フィールドがない場合に作成されます。
他のヒント
あなたのコードの最初のすべてのフィールドを作成からの何も防止できます、
その後、あなたの計算フィールドを追加します。の
は次のいずれかの「ハッキングタイプ」保護CreateFieldsを使用するを使用することができます
type
THackQuery = class(TADOQuery)
end;
[...]
MyQuery.FieldDefs.Update;
THackQuery(MyQuery).CreateFields;
や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]);
そして、あなたの計算フィールドを作成します:
MyQueryMyField := TStringField.Create(MyQuery);
with MyQueryMyField do
begin
Name := 'MyQueryMyField';
FieldKind := fkCalculated;
FieldName := 'MyField';
Size := 10;
DataSet := MyQuery;
end;
あなたはあなたの計算フィールドに加えて、すべてのフィールドを追加する必要があります。
あなたはフィールドを追加すると、あなたは、データセットにしたいことをすべてのフィールドを追加する必要があります。
Delphiは、動的フィールド対この持続的項目を呼び出します。すべてのフィールドは、永続的または動的です。残念ながら、あなたは、両方の混合物を持っていないことができます。
ドキュメントからノートへのもう一つは、
であります永続フィールドコンポーネントリストです お使いのアプリケーションに保存されている、とそうでありません 変更した場合でも、Aの構造 データベースのデータセットは、基礎となります 変更します。
だから、あなたは後で、テーブルにフィールドを追加する場合は、コンポーネントに新しいフィールドを追加する必要があります、注意してください。フィールドを削除しても同じこと。
あなたが本当に持続フィールドを使用しない場合は、、別の解決策があります。計算フィールドを表示する必要があります任意のグリッドまたはコントロールでは、カスタム、それを描くことができます。例えば、多くのグリッドコントロールはOnCustomDrawイベントを持っています。あなたはそこにあなたの計算を行うことができます。
、あなたはそのようなものを使用することができます。
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;