ソート、検索、フィルタリングに関する質問(FireDAC)
FAQ(FireDAC) への移動
このトピックでは、データセットのソート、検索、フィルタリングに関係する質問と回答の一覧を扱います。
Q1: データセットの英語以外の文字列が正しくソートできません。"[FireDAC][DatS]-2. オブジェクト [] が見つかりません" というエラーも出力されます。どこが間違っているのでしょうか。
A: この問題を修正する手順は以下のとおりです。
- $(BDS)\source\data\firedac\FireDAC.inc を開きます。
- 次の行を探します。
{$define FireDAC_NOLOCALE_DATA} // define to use binary data comparison
{$define FireDAC_NOLOCALE_META} // define to use binary metadata comparison
- これらの行をコメントにします。
- ファイルを保存し、アプリケーションを再コンパイルします。
メモ: 英語の(ASCII)テキストだけを扱っている場合には、上記の行をコメントにしないでください。その方が、ソートや検索の操作が大幅に速くなります。
Q2: データを順に並べるにはインデックスを作成する必要があるでしょうか。
A: データを順に並べるだけであれば、インデックスを定義する必要はありません。IndexFieldNames で十分です。インデックスを使用すると、フィルタリングや順序付けの機能を持つビューを定義することができます。ただし、どの FireDAC データセットでも、インデックスと IndexDefs は相互排他的です。つまり、作成できるのはインデックスと IndexDefs のどちらかだけで、両方はできません。
Q3: インデックス定義内でルックアップ フィールドを使用することはできるでしょうか。
A: できません。ただし、インデックス内で内部計算フィールドを使用することはできます。そのためには、永続フィールドを追加し、fkInternalCalc フィールドを追加し、OnCalcFields イベント ハンドラを作成して、イベント ハンドラ内でそのフィールドを計算します。以下に例を示します。
procedure TForm21.FDTable1CalcFields(DataSet: TDataSet);
begin
DataSet.FieldByName('f_calc').AsString :=
FDMemTable2.Lookup('code',
DataSet.FieldByName('f_code').AsInteger, 'name');
end;
Q4: インデックス定義内で計算フィールドを使用することはできるでしょうか。
A: FireDAC の現在のリリースでは、Locate や Lookup や IndexFieldNames などで FieldKind が fkCalculated のフィールドを使用することはサポートされていません。回避策として、fkInternalCalc フィールドを使用してください。
Q5: 実行時に FindNearest メソッドを呼び出すと、アクティブなインデックスがないというエラー メッセージが出力されます。
A: インデックスは、アクティブであるだけでなく、選択されていなければなりません。そのためには、次のいずれかの設定を行ってください。
- Indexes[i].Selected := True
- FDTable1.IndexFieldName := <インデックス名>
Q6: TADDataset のインデックス用に異なる文字照合順序を定義するにはどうすればよいでしょうか。
A: 一般に、次の 3 つの方法があります。
- LCID を TFDMemTable.Table.Locale に割り当てます。FireDAC は、SORT_STRINGSORT フラグを引数に CompareStringA や CompareStringW を呼び出します。デフォルト照合順序は、どの DBMS の場合でも LOCALE_USER_DEFAULT です。詳細は、Win API ドキュメントを参照してください。
- ソース コード(FireDAC.DatS.pas、TFDDatSRow.CompareData)を変更して、独自の比較アルゴリズムを実装します。
- 式評価モジュールにカスタム関数を登録します。登録の詳細は FireDAC.Stan.Expr.pas を参照してください。登録すると、その関数を TFDMemTable.Indexes[..].Expression で使用することができます。たとえば、Expression := 'MySort(Name)' などです。
今後、カスタム照合順序が実装される予定です。質問者のように SQLite ドライバを使用している場合には役立つと思われます。
Q7: COL1 降順、COL2 昇順のようにデータを並べるにはどうすればよいでしょうか。
A: IndexFieldNames := 'col_1:D;col_2'; とします。
Q8: SQL Server を使用していますが、FDQuery.Locate で "Function sequence error"(関数シーケンス エラー)が発生します。どこが間違っているのでしょうか。
A: 明示的にトランザクションが開始された後で FDQuery が開かれ、それから、トランザクションがコミットされたものと思われます。その後、FDQuery.Locate を呼び出していますが、すべてのレコードを FDQuery から取得したわけではありません。その代わりに Locate で暗黙的に FetchAll が呼び出されています。SQL Server でエラーが発生しているのはこのような理由です。
これを回避するには、次のようにします。
- 具体的な FDQuery について、FDQuery.FetchOptions.Mode = fmAll に設定します。
- その FDQuery の FetchAll を呼び出し、それから Commit を呼び出します。
SQL Server は、Commit/Rollback を行った後で、開いているカーソルを無効にします。
Q9: ルックアップ テーブルを実装するにはどうするのが一番よいでしょうか。開いたり読み込むのに時間がかかるので、サイズが大きいように思えます。
A: クライアント上のすべてのレコードが必要なのでなければ、TFDQuery を使って SELECT ... WHERE ... を実行してください。
Q10: パフォーマンスを最大にするためのルックアップ フィールドの扱い方について、ヒントがあれば教えてください。
A: TFDQuery.IndexFieldNames を LookupKeyFields に設定すると、FireDAC ではクライアント インデックスを使ってルックアップ データセット内のレコードを検索するようになります。
一意のキー値の数が多くなければ、LookupCache を True に設定してください。
Q11: DateTime 値でのフィルタリングに失敗します。どこが間違っているのでしょうか。
Q: Delphi 2007 で FireDAC 2.0.11.895 を使用して Postgres データベースを扱っています。DateTime フィールドのフィルタリングを行うと、次のようにして渡した値と正確に一致するものを取得できないようです。
created_date = '8/10/2009 14:42:14' // or even...
created_date = '8/10/2009 14:42:14.247' // ... the exact time with millisec
その代わりに、次のようにしなければなりません。
created_date >= '8/10/2009 14:42:14' AND created_date < '8/10/2009 14:42:15' or
created_date >= '8/10/2009 14:42:14.000' AND created_date <= '8/10/2009 14:42:14.999'
A: DBMS や DBMS API で時刻の値が丸められる場合があることが問題です。プログラマ側では .247 を小数部分と見なしてほしいと思っていても、.246 などになる可能性があります。これは FireDAC の問題ではなく、DBMS の動作です。
次のように、時刻値を秒の単位に丸め、その値と小数部分を持たない定数とを比較することができます。
uses
FireDAC.Stan.ExprFuncs;
...
FDQuery1.Filter = 'TimeStampDiff(''second'', created_date,
convert(''timestamp'', ''8/10/2009 14:42:14'')) = 0';