コマンドの実行(FireDAC)
コマンドの操作(FireDAC) への移動
目次
TFDConnection の使用
TFDConnection には ExecSQL メソッドがあります。これは、簡単に使用できるオーバーロードされたメソッド群で、次のような場合に便利です。
- 結果セットが返されない。
- SQL コマンドは 1 度しか実行されないため、準備済みの状態で格納しておく必要がない。
- 高度なパラメータのセットアップが必要ない。
- 設計時の SQL コマンドのセットアップが必要ない。
たとえば、DDL コマンドを実行するには、次のコードを使用します。
FDConnection1.ExecSQL('drop table testtab');
FDConnection1.ExecSQL('create table testtab (id integer, name varchar(10))');
FDConnection1.ExecSQL('insert into testtab values (1, ''FireDAC'')');
パラメータを取るクエリを実行するには、オーバーロードされた別のメソッドを使用します。
FDConnection1.ExecSQL('insert into testtab values (:id, :name)', [1, 'FireDAC']);
また、TFDConnection には、ExecSQL と違って 1 つの結果セット値を返す、ExecSQLScalar メソッドがあります。
sName := FDConnection1.ExecSQLScalar('select name from testtab where id = :id', [1]);
TFDQuery の使用
一般に、TFDQuery は、設計時または実行時あるいはその両方でセットアップします。
設計時の TFDQuery のセットアップ
設計時に TFDQuery をセットアップするには、まずそれをフォームにドロップします。TFDQuery.Connection は、このフォームに TFDConnection があれば、それを指すように自動的に設定されます。それから、TFDQuery をダブルクリックして、[FireDAC クエリ エディタ]を起動します。
ここで、SQL テキストと、必要であればパラメータやマクロの値を指定し、オプションを設定することができます。
[実行]ボタンを押すと、クエリが実行されます。コマンドから結果セットが返されると、その結果セットが[レコードセット]ペインに、結果セットの構造が[構造]ペインに表示されます。DBMS からコマンドについてのメッセージや警告が返されると、[メッセージ]ペインに表示されます。[次のレコードセット]ボタンを使用して、コマンドから返されたすべての結果セットを順に調べることができます。
UPDATE/INSERT/DELETE などの DML コマンドのテストを行う場合には、[自動ロールバック]チェック ボックスをオンにして、コマンドで実行したアクションを自動的にロールバックする必要があります。
[OK]を押すと、変更内容が TFDQuery に格納されます。
パラメータの使用
パラメータを取るクエリの使用は、SQL データベース アプリケーション開発におけるベスト プラクティスの 1 つです。主な利点には次のようなものがあります。
- 1 つの SQL コマンドを作成し、パラメータ値を変えて複数回実行することができます。DBMS は、コマンド実行計画を 1 度しか作成しません。その結果、DBMS エンジンの負荷が低下します。
- 次にコマンドを実行するときには、パラメータ値だけが DBMS エンジンに転送されます。その結果、ネットワークの負荷が低下します。
- Microsoft Access SQL で日付定数を記述する方法など、SQL 定数の正しい形式を気にする必要がなくなります。
パラメータ マーカーを SQL テキストに含めるには、:<名前> 構文を使用します。その後、Params コレクションを使って該当する値を割り当てます。以下に例を示します。
FDQuery1.SQL.Text := 'select * from tab where code = :Code';
FDQuery1.ParamByName('code').AsString := '123';
FDQuery1.Open;
クエリを実行する前に、Name、DataType、ParamType の各パラメータの値を指定する必要があります。また、Position も設定してください。
SQL プロパティを割り当てたときに ResourceOptions.ParamCreate が True であれば、Params コレクションの内容は自動的に設定されます。
また、Params コレクションの内容はコードで設定することもできます。そのためには、ResourceOptions.ParamCreate を False に設定して、自動パラメータ作成をオフにしておく必要があります。以下に例を示します。
FDQuery1.ResourceOptions.ParamCreate := False;
FDQuery1.SQL.Text := 'select * from tab where code = :Code';
with FDQuery1.Params do begin
Clear;
with Add do begin
Name := 'CODE';
DataType := ftString;
Size := 10;
ParamType := ptInput;
end;
end;
パラメータと SQL コマンドは、名前または位置でバインドすることができます。どちらを使用するかは、Params.BindMode プロパティに以下の値を設定して制御します。
- pbByName。Params コレクション内のパラメータは、対応するパラメータ マーカーと Name プロパティでバインドされます。SQL コマンド内に同じ名前のパラメータ マーカーがある場合、1 つのインスタンスのみが Params 内に含まれます。
- pbByNumber。Params コレクション内のパラメータは、SQL コマンド テキスト内のマーカーと Position プロパティでバインドされます。SQL コマンド内に同じ名前のパラメータ マーカーがある場合、そのすべてが Params 内に含まれます。パラメータの Position が設定されていなければ、コマンドの準備時にパラメータ インデックスを使って自動的に割り当てられます。Position は 1 から始まります。
DataType は、パラメータ値を Value または AsXxxx プロパティに割り当てることで、明示的または暗黙的に指定されます。そうでなければ、FormatOptions.DefaultParamDataType が使われます。
ParamType が指定されなければ、ResourceOptions.DefaultParamType が使われます。デフォルトでは、すべてのパラメータが入力パラメータになります。出力パラメータを使用するには、ParamType を明示的に指定しなければなりません。
入力パラメータの値は、クエリの実行前に設定しなければなりません。設定は、以下のいずれかの方法を選んで行います。
- 明示的にパラメータ値を割り当てる方法:
FDQuery1.ParamByName('code').AsString := 'DA1001';
FDQuery1.Open;
- オーバーロードされた ExecSQL メソッドまたは Open メソッドのいずれかを使用する方法:
FDQuery1.Open('', ['DA1001']);
パラメータ値を Null に設定するには、パラメータのデータ型を指定してから Clear メソッドを呼び出します。
with FDQuery1.ParamByName('name') do begin
DataType := ftString;
Clear;
end;
FDQuery1.ExecSQL;
出力パラメータの値には、クエリの実行後にアクセスできます。SQL コマンドや DBMS の中には、まずすべてのコマンドの結果セットを処理し、その後で出力パラメータ値が渡されるものがあるかもしれません。出力パラメータ値を強制的に取り出すには、TFDAdaptedDataSet.GetResults メソッドを使用します。また、Firebird および Oracle の RETURNING 句の出力パラメータについては、「RETURNING 句の統一サポート」のトピックを参照してください。
クエリの準備
クエリは、実行する前に準備しなければなりません。FireDAC では、準備時に以下の処理を自動的に実行します。
- 接続をアクティブまたはオンラインの状態にします。
- すべてのパラメータが正しく設定されているかを確認します。
- コマンド テキストのプリプロセスを行います。
- コマンド テキストに RecsSkip や RecsMax を指定します(オプション)。
- Firebird/Interbase DBMS にアクティブな接続がなければ、トランザクションを開始します(オプション)。
- 最終的なコマンド テキストを DBMS API に転送します。最終的なテキストは、TFDQuery.Text プロパティを使って取得できます。
コマンドの準備が済んでいるときには、接続はアクティブになり、クエリはサーバー リソースを保持しているはずです。クエリの準備を解除してすべてのリソースを解放するには、Unprepare メソッドまたは Disconnect メソッドを使用します。
コマンドを 1 度しか実行する必要がない場合には、ResourceOptions.DirectExecute を True に設定してください。そうすると、DBMS によっては(SQL Server、SQL Anywhere)、SQL コマンドを準備するという負担の大きい操作を避けられるため、実行速度が向上します。
クエリの実行
結果セットを返さないクエリを実行するには、ExecSQL メソッドを使用します。クエリが結果セットを返す場合は、"[FireDAC][Phys][MSAcc]-310。結果セットを返すコマンドは実行できません" という例外が発生します。
結果セットを返すクエリを実行し、その結果セットを開くには、Open メソッドを使用します。クエリが結果セットを返さない場合は、"[FireDAC][Phys][MSAcc]-308。結果セットを返さないコマンドを開くまたは定義することができません" という例外が発生します。
アドホック クエリを実行するには、OpenOrExecute メソッドを使用します。
例外 "パラメータのデータ型が変更されました"
クエリの Prepare/ExecSQL/Open が最初に呼び出された後でパラメータの型が変更されると、その後の呼び出し時に FireDAC で次の例外が発生します。
[FireDAC][Phys][IB]-338. Parameter [Xxxx] data type is changed.
Query must be reprepared.
Prepare/ExecSQL/Open の最初の呼び出し時に、FireDAC はパラメータのデータ型を記憶します。このデータ型は、TFDParam.DataType プロパティを設定して明示的に指定することも、TFDParam.AsXxxx または TFDParam.Value プロパティを使って値を割り当てることで暗黙的に指定することもできます。次に ExecSQL/Open を呼び出す前にアプリケーションでパラメータのデータ型を変更すると、ExecSQL/Open を再び呼び出したときに "パラメータ [xxxxx] のデータ型が変更されました" という例外が発生します。
TFDParam.Value プロパティの場合、次の呼び出し時にパラメータのデータ型は変更されません。割り当てられた値が現在のデータ型にキャストされます。そのため、上記の例外を回避するには、以下を使用してください。
- TFDParam.Value プロパティの値
- TFDParam.AsXxxx プロパティ(Xxxx は初めて使用されたもの)
DBMS からのフィードバックの取得
TFDQuery.RowsAffected プロパティを使用して、コマンドで処理された行数(たとえば DELETE コマンドで削除された行数など)を取得することができます。
コマンドからエラーが返されると、例外が発生します。詳細は、「エラーの処理」のトピックを参照してください。例外は、次の 3 つのいずれかの方法で処理することができます。
- try/except/end 構文要素を使用する。以下に例を示します。
try
FDQuery1.ExecSQL;
except
on E: EFDDBEngineException do
; // do something here
end;
- TFDQuery.OnError イベント ハンドラを設定する。
- TFDConnection.OnError イベント ハンドラを設定する。以下に例を示します。
procedure TForm1.FDConnection1Error(ASender: TObject; const AInitiator: IFDStanObject;
var AException: Exception);
begin
if (AException is EFDDBEngineException) and (EFDDBEngineException(AException).Kind = ekRecordLocked) then
AException.Message := 'Please, try the operation later. At moment, the record is busy';
end;
FDConnection1.OnError := FDConnection1Error;
配列 DML の場合には、エラー処理はさらに複雑になります。
また、DBMS によっては、コマンドから警告やヒントやメッセージが返されます。メッセージ処理を有効にするには、ResourceOptions.ServerOutput を True に設定します。メッセージを処理するには TFDConnection.Messages プロパティを使用します。以下に例を示します。
var
i: Integer;
begin
FDConnection1.ResourceOptions.ServerOutput := True;
FDQuery1.ExecSQL;
if FDConnection1.Messages <> nil then
for i := 0 to FDConnection1.Messages.ErrorCount - 1 do
Memo1.Lines.Add(FDConnection1.Messages[i].Message);
end;
SQL Server など一部の DBMS では、メッセージを追加の結果セットとして返します。そのため、アプリケーションでメッセージを処理するには、複数の結果セットを扱う必要があります。次に示すのは、SQL Server の状態とメッセージを使用する複雑な例です。ここでは、TFDMemTable を使って結果セットに行を格納しています。
var
i: Integer;
begin
FDConnection1.ResourceOptions.ServerOutput := True;
FDQuery1.FetchOptions.AutoClose := False;
FDQuery1.Open('select * from Region; print ''Hello''');
FDMemTable1.Data := FDQuery1.Data;
Memo1.Lines.Add(Format('%d rows processed', [FDMemTable1.RecordCount]));
FDQuery1.NextRecordSet;
if FDConnection1.Messages <> nil then
for i := 0 to FDConnection1.Messages.ErrorCount - 1 do
Memo1.Lines.Add(FDConnection1.Messages[i].Message);
end;
クエリ実行とトランザクション
デフォルトで、SQL コマンドはすべて自動コミット モードで実行されます。つまり、コマンドを実行する直前にアクティブなトランザクションがなければ、暗黙のトランザクションが開始します。同様に、コマンド実行の直後に、そのトランザクションは次のように終了します。
- 実行が成功した場合にはコミットで
- 失敗した場合にはロールバックで
Firebird および Interbase の場合には、クエリの準備の直前に暗黙のトランザクションが開始します。1 つのコマンド実行を明示的なトランザクションで囲む必要はなく、自動コミット モードを使用するだけで構いません。
拡張機能
ほとんどのデータベース アプリケーションにはバックエンドの管理用ユーティリティがあり、そこで SQL スクリプトを実行しなければなりません。これらのスクリプトは、DBMS の SQL スクリプト構文に適した方法で行に記述します。SQL スクリプトを実行するには、TFDScript コンポーネントを使用します。
ときには、データベース アプリケーションで、さまざまなデータベースのテーブルを使って異種クエリを実行したり、データベース テーブルではなく TDataSet の下位クラスに対して SQL コマンドを実行することがあります。そのようなクエリを実行するには、ローカル SQL エンジンおよび TFDLocalSQL コンポーネントを使用します。