更新コマンド生成(FireDAC)
データの編集(FireDAC) への移動
目次
概要
データベース アプリケーションで以下のデータセット メソッドを呼び出す場合、更新モードは異なります。
- Insert/Edit/Post/Delete -- 即時更新モード
- ApplyUpdates -- キャッシュ更新モード
また、データを更新する SQL コマンドは、FireDAC で自動的に生成されます。FireDAC の SQL コマンド ジェネレータは、データベースの ID フィールド、シーケンス、トリガ、特殊なデータ型(Oracle の BLOB/CLOB/BFILE など)を認識し、接続先の DBMS に応じて効率的な SQL コマンドを生成します。これにより、開発者が手動作成の SQL コマンドを使用しなければならないケースが少なくなります。FireDAC では、開発者は必ず TFDUpdateSQL を使用しなければならないわけではありませんが、これを使用すると、更新 SQL コマンドをオーバーライドできます。
たとえば、Oracle テーブル(ID フィールドがシーケンスからトリガで設定され、IMAGE が BLOB 型)に新しいレコードをポストする場合、FireDAC により、以下の SQL コマンドが生成されます。
INSERT INTO OracleTab (NAME, DT, IMAGE)
VALUES (:NEW_NAME, :NEW_DT, EMPTY_BLOB())
RETURNING :NEW_ID, :NEW_IMAGE
1. FROM、INTO、UPDATE の各句
FireDAC では、SELECT ... FROM ... 文のメイン テーブル(最初のテーブル)を更新テーブル名として使用します。また、このテーブルは mkPrimaryKeyFields メタデータの取得にも使用されます。UpdateOptions.UpdateTableName を使用すると、更新テーブルを明示的に指定できます。これが必要なのは以下の場合です。
- データセットが TFDStoredProc である。
- TFDQuery に SELECT クエリが含まれていない。
- FireDAC が更新テーブル名をクエリから正しく取得できない。
- アプリケーションで更新を特定のテーブルにリダイレクトする必要がある。
2. WHERE 句
UpdateOptions.UpdateMode では、更新および削除をポストするための WHERE 句の生成を制御します。デフォルト値 upWhereKeyOnly の場合は、WHERE 句で一意識別列のみ使用され、更新行を効率的かつ安全に特定できます。一意識別列が指定されず、行を識別する列も見つからない場合、FireDAC では UpdateOptions.UpdateMode を upWhereAll
に切り替えます。WHERE 句に以下のようなフィールドが含まれている場合は、"行が見つかりません" というエラーが発生するおそれがあります。
- DOUBLE 型、FLOAT 型、TIME 型、DATETIME 型のフィールドやその他の浮動小数点ベース フィールドの場合、値の比較で精度が落ちるおそれがあります。
- テキスト フィールドに無効なエンコーディングや余分なスペースがある場合、比較に失敗するおそれがあります。
- その他似たような障害が発生するおそれがあります。
そのような場合、アプリケーションでは以下の例外が発生します。
[FireDAC][DApt]-400. Update command updated [0] instead of [1] record.
同様に、WHERE 句で指定されている列で行が一意に識別されない場合は、複数のレコードが更新される可能性があります。その場合、次のような例外が発生します。
[FireDAC][DApt]-400. Update command updated [4] instead of [1] record.
これらの問題を解決するには、以下を検討します。
- 正しい一意識別列を指定する。
- 対応する TField.ProviderFlags プロパティから
pfInWhere
を取り除くことで、WHERE 句で使用されている一部のフィールドを無効にする。 - UpdateOptions.CountUpdatedRecords を False に設定することで、これらのエラーが発生しないようにする。
メモ: (トリガも含まれている)SQL Server テーブルで上記の問題を回避するには、それらのトリガの冒頭で SET NOCOUNT ON が指定されていなければなりません。それが不可能な場合は、UpdateOptions.CountUpdatedRecords を False に設定します。
3. SET 句と VALUES 句
UpdateOptions.UpdateChangedFields を使用すると、UPDATE SET ... 句または INSERT VALUES ... 句に含まれるフィールドを制御できます。これを True に設定すると、変更されたフィールドのみこれらの句に含まれることになり、以下の役に立ちます。
- 更新のポスト時のトラフィックを最小限に抑える。
- 不必要な制約検証を避ける。
- 余分なトリガ発生を避ける。
- REDO ログの生成を最小限に抑える。
これを False に設定すると、これらの句にすべてのフィールドが含まれることになり、生成された SQL 文と同じものをすべての更新のポストに再利用して、SQL 文を準備するための DBMS 処理をできるだけ少なくするのに役に立ちます。
列への更新を無効にするには、対応する TField.ProviderFlags プロパティから pfInUpdate
を取り除きます。
4. RETURNING と追加の SELECT
UpdateOptions.RefreshMode = rmOnDemand
の場合は、列値の自動更新を制御できます。列値は、レコードの挿入後または更新後に DBMS により変更される可能性があります。レコードの挿入後に更新が必要な場合がある列は以下のとおりです。
- 自動インクリメント列
- データベース側で値が計算される列
- デフォルト値のある列
- 行を識別する列
- タイムスタンプ列
- トリガで更新される列
レコードの更新後に更新が必要な場合がある列は以下のとおりです。
- データベース側で値が計算される列
- タイムスタンプ列
- トリガで更新される列
DBMS の機能によっては、以下のように追加の句/コマンドが生成され、更新する列の値を返します。
- Oracle、Firebird、PostgreSQL -- RETURNING 句
- DB2 -- SELECT ... FROM FINAL TABLE 句
- SQL Server、SQL Anywhere、SQLite -- 追加の SELECT コマンドを含んだ SQL バッチ
- その他の DBMS -- 追加の SELECT コマンド
5. 更新コマンドのキャッシング
UpdateChangedFields を False に設定すると、パフォーマンスが向上する場合があります。また、UpdateOptions.FastUpdates プロパティなどの他のいくつかの設定と組み合わせて、追加クエリを避け、生成された更新コマンドをキャッシュできるようにすることで、更新のポスト時のパフォーマンスをさらに向上させることができます。
FastUpdates = True は以下と同等です。
- UpdateChangedFields = False
- UpdateMode =
upWhereKeyOnly
- LockMode =
lmNone
- RefreshMode =
rmManual
- CheckRequired = False