Sign in Join
Wiki copytemporary

SqlDBHowto/ja: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11このテキストは「how-to」として作られている。数多くの質問に1つ、1つ答え、どのように様々なクラスを用いるのか説明したい。これらすべての質問は次から次へと沸き起こり、ある種のチュートリアルとなった。
22
33このテキストがFree Pascal同様にLazarusでも用いられるように書くつもりだが、例はFree Pascal(即ち、コンソールアプリケーション)に向けてある。
44
55## どこで公式のドキュメンテーションが見つかるか
66
77公式ドキュメンテーションはここを参照されたい[SQLDBドキュメンテーション](http://www.freepascal.org/docs-html/fcl/sqldb/index.html)。
88
99## どのようにしてデータベースサーバに接続するか
1010
1111SqlDBは直接データベースサーバには接続せず、用いられているデータベースサーバに対応するクライアントを用いる。SqlDBはクライアントライブラリにコマンドを送り、クライアントライブラリがデータベースサーバに接続しコマンドを送る。これはデータベースと接続を持つためにクライアントライブラリがインストールされていなければならないことを意味する。Windowsではクライアントライブラリは通常、.dll、Linux では.so、OS/Xでは.dylibである。
1212
1313クライアントライブラリが適切にインストールされているときには、TSQLConnectionコンポーネントを用いてデータベースサーバに接続できる。様々なTSQLConnectionコンポーネントが異なるデータベースサーバに対して利用可能である([[SQLdb Package|SQLdb_Packageを]]参照):
1414
1515- Firebird/Interbase: [[TIBConnection]]
1616- MS SQL Server: [[TMSSQLConnection]] (FPC 2.6.1から利用可能)
1717- MySQL v4.0: [[TMySQL40Connection]]
1818- MySQL v4.1: [[TMySQL41Connection]]
1919- MySQL v5.0: [[TMySQL50Connection]]
2020- MySQL v5.1: [[TMySQL51Connection]] (FPC version 2.5.1から利用可能)
2121- MySQL v5.5: [[TMySQL55Connection]] (Lazarus 1.0.8/FPC version 2.6.2から利用可能)
2222- MySQL v5.6: [[TMySQL56Connection]] (Lazarus 1.2.4/FPC version 2.6.4から利用可能)
2323- MySQL v5.7: [[TMySQL57Connection]] (訳注:いつから?)
2424- ODBC: [[TODBCConnection]] ([[ODBCConn|ODBCConn#TODBCConnectionを]]参照)
2525- Oracle: [[TOracleConnection]] ([[Oracle|Oracleを]]参照)
2626- PostgreSQL: [[TPQConnection]] ([[postgresql|postgresql#SQLDBを]]参照)
2727- Sqlite3: [[TSQLite3Connection/ja]] (FPC version 2.2.2から利用可能、[[SQLite|SQLite#Built-in_SQLDBを]]参照)
2828- Sybase ASE: [[TSybaseConnection]] (FPC 2.6.1から利用可能、[[Lazarus Database Overview|LazarusとMSSQL/Sybase]])
2929
3030MySQLに対する注意 - クライアントとコネクションが相互交換できないほどクライアントバージョン間に多くの相違がある。もしクライアントライブラリ バージョン 4.1がインストールされているとすると、TMySQL41Connectionを用いなければならない。これはMySQLサーバには関連しておらずMySQL 4.1クライアントライブラリを用いて、おそらくMySQL 5.0サーバに接続できる(どの組み合わせがサポートされているかはMySQLドキュメンテーションを参照されたい)。
3131
3232さまざまなデータベースで細部は異なるが、一般的にデータベースサーバに接続するために4つのプロパティを設定しなければならない:
3333
3434- サーバの名前とIPアドレス
3535- データベースの名前
3636- ユーザー名
3737- パスワード
3838
3939これらのプロパティが設定されると、サーバに「open」メソッドを作ることができる。もし接続に失敗するとEDatabaseError例外が生じる。データベースサーバに接続が確立しているかどうか確かめるために、「connected」プロパティを用いること。
4040
4141``` pascal
4242Program ConnectDB;
4343
4444{$mode objfpc}{$H+}
4545
4646uses
4747 IBConnection;
4848
4949function CreateConnection: TIBConnection;
5050begin
5151 result := TIBConnection.Create(nil);
5252 result.Hostname := 'localhost';
5353 result.DatabaseName := '/opt/firebird/examples/employee.fdb';
5454 result.UserName := 'sysdba';
5555 result.Password := 'masterkey';
5656end;
5757
5858var
5959 AConnection : TIBConnection;
6060
6161begin
6262 AConnection := CreateConnection;
6363 AConnection.Open;
6464 if Aconnection.Connected then
6565 writeln('Successful connect!')
6666 else
6767 writeln('This is not possible, because if the connection failed, ' +
6868 'an exception should be raised, so this code would not ' +
6969 'be executed');
7070 AConnection.Close;
7171 AConnection.Free;
7272end.
7373```
7474
7575もし、エラーが発生したら、注意してエラーメッセージを読むこと。データベースサーバが稼働していない、ユーザー名もしくはパスワードが誤っている、データベース名またはIPアドレスが誤って入力されたかもしれない。もしエラーメッセージがクライアントライブラリが見つからないと言っているなら、クライアントライブラリが正しくインストールされているか確かめること。しばしばエラーメッセージは探しているファイル名を文字通り言う。
7676
7777## どのようにして直接クエリを実行/テーブルを作るのか
7878
7979SqlDB - 文字通り名前がすべてを体現している - はSQLを用いて唯一、データベースサーバとやり取りをする。SQL は 「Structured Query Language」を意味する。 SQLはリレーショナルデータベースとやり取りすることを可能にするために開発された言語である。実際にはすべてのデータベースシステムはそれぞれ方言を持っているが、SQLステートメントの多くはすべてのデータベースシステムで共通している。
8080
8181FPCでは、以下の違いがある:
8282
8383- 情報(データセット)を返すSQLステートメント。このため、TSQLQueryコンポーネントを用いなければならない; #How to read data from a table?を参照のこと。
8484- 情報を返さずに、変わって何かを行うステートメント、例えば、データを更新する。このため、またTSQLConnectionの「ExecuteDirect」メソッドを使うかもしれない(データセットを引き戻すためにこれを使うこともできるが、結果に興味はない、例えばセレクタブルストアドプロシージャ)。
8585
8686大半のデータベースシステムはトランザクションでSQLステートメントを実行する。もし、他のトランザクションの中で利用できるトランザクションの中で行われる変更を望む、もしくはトランザクションが閉じた後でそれらの変更する(!)ならば、トランザクションを「commit」しなければならない。
8787
8888トランザクションをサポートするために、SqlDBはTSQLTransactionコンポーネントを含んでいる。SqlDBによって実行されるSQLステートメントはデータベースシステムが、トランザクションをサポートしていなくとも、常にトランザクションの最中に行われる必要がある。同様に、TSQLTransactionがいまだにサポートしていないトランザクションをサポートしているデータベースシステムも存在する。その時ですらTSQLTransactionコンポーネントを使わなければならない。
8989
9090SQLステートメントを実行するために`TSQLConnection.ExecuteDirect`を使うには、どの「Transaction」が使われなければならないかを明示しなければならない。逆に、TSQLTransactionを使うためには、どのTSQLTransactionコンポーネントが使われるかを明示しなければならない。
9191
9292以下の例は「NAME」と「ID」フィールドを持つ「TBLNAMES」を生成し2つのレコードを挿入する。今回はSQLiteを用いる。使われるSQLステートメントは説明されない。SQLステートメント、その使用と文法に関しては、データベースシステムのドキュメンテーションを参照されたい。 この例ではいかなるエラーの捕捉も試みないことに注意すること、それは悪いことである! [例外](https://www.freepascal.org/docs-html/current/ref/refch17.html#x226-24800017)を調べていただきたい。
9393
9494``` pascal
9595program CreateTable;
9696{$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
9797uses
9898 sqldb, sqlite3conn;
9999
100100var
101101 AConnection : TSQLite3Connection;
102102 ATransaction : TSQLTransaction;
103103
104104begin
105105 AConnection := TSQLite3Connection.Create(nil);
106106 AConnection.DatabaseName := 'test_dbase';
107107
108108 ATransaction := TSQLTransaction.Create(AConnection);
109109 AConnection.Transaction := ATransaction;
110110 AConnection.Open;
111111 ATransaction.StartTransaction;
112112 AConnection.ExecuteDirect('create table TBLNAMES (ID integer, NAME varchar(40));');
113113
114114 ATransaction.Commit;
115115
116116 ATransaction.StartTransaction;
117117 AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (1,''Name1'');');
118118 AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (2,''Name2'');');
119119 ATransaction.Commit;
120120 AConnection.Close;
121121 ATransaction.Free;
122122 AConnection.Free;
123123end.
124124```
125125
126126## どのようにしてテーブルからデータを取得するのか
127127
128128テーブルからデータを読み込むときにはTSQLQueryを用いること。TSQLQueryコンポーネントはその仕事をするために、TSQLConnectionコンポーネントとTSQLTransactionコンポーネントに接続されなければない。TSQLConnectionとTSQLTransactionの設定は#How to connect to a database server?と#How to execute direct queries/make a table?で議論する。
129129
130130TSQLConnectionの時、TSQLTransactionとTSQLQueryが接続される、そしてTSQLQueryは動作するためにさらに設定される。TSQLQueryはTStringsオブジェクトを含む「SQL」プロパティを持つ。「SQL」プロパティは実行されるべきSQLステートメントを含んでいる。もしテーブル`tablename`のすべてのデータを読まなければならないときには、「SQL」プロパティを以下のように設定する:
131131
132132``` sql
133133'SELECT * FROM tablename;'
134134```
135135
136136サーバからテーブルを読み込み、TSQLQueryデータセットにデータを読み込むには「open」を使うこと。データは「close」を用いてクエリが閉じられるまでTSQLQueryを通してアクセスできる。
137137
138138TSQLQueryはTDatasetのサブクラスである。TDatasetはテーブルのすべてのカラムを含む「Field」コレクションを持っている。TDatasetはまた、現在のレコードを追跡する。現在のレコードを変更するために「First」、「Next」、「Prior」、「Last」を用いること。もし最初のレコードが届いたら「Bof」が「True」となり、もし最後のレコードに達したら、「Eof」が「True」になる。現在のレコードの値を読むためには初めに正しい「TField」オブジェクトを見つけ、そして「AsString」、「AsInteger」を用いること。
139139
140140### 例: テーブルからデータを取得する
141141
142142以下は上のCreateTable例で作られたテーブルのすべての値を表示する例である。test_databaseファイルをShowDataワーキングディレクトリにコピーすること。エラーチェックを行っていないことに再び注意すること!
143143
144144``` pascal
145145Program ShowData;
146146 {$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
147147uses
148148 DB, Sysutils, sqldb, sqlite3conn;
149149
150150var
151151 AConnection : TSQLConnection;
152152 ATransaction : TSQLTransaction;
153153 Query : TSQLQuery;
154154
155155begin
156156 AConnection := TSQLite3Connection.Create(nil);
157157 ATransaction := TSQLTransaction.Create(AConnection);
158158 AConnection.Transaction := ATransaction;
159159 AConnection.DatabaseName := 'test_dbase';
160160 Query := TSQLQuery.Create(nil);
161161 Query.SQL.Text := 'select * from tblNames';
162162 Query.Database := AConnection;
163163 Query.Open;
164164 while not Query.Eof do
165165 begin
166166 Writeln('ID: ', Query.FieldByName('ID').AsInteger, 'Name: ' +
167167 Query.FieldByName('Name').AsString);
168168 Query.Next;
169169 end;
170170 Query.Close;
171171 AConnection.Close;
172172 Query.Free;
173173 ATransaction.Free;
174174 AConnection.Free;
175175end.
176176```
177177
178178もちろん、上記のコードは未完成で、「try...finally」ブロックを欠く。しかし、上のコードはデータベースコードを示す意図のもので、そのため仕上げの一筆は省いてある。 「TSQLTransaction.StartTransaction」が使われてないことに注意してほしい。これは不要である。TSQLQueryが開かれるとき、SQLステートメントが実行され、もしトランザクションが利用できないときにはトランザクションが自動的に始まる。プログラマは明示的にトランザクションを開始させる必要はない。 同様のことがTSQLConnectionによって維持されている接続にも適用される。接続は必要に応じて開かれ、「Aconnection.Open」は全く必要ない。もし、TSQLTransactionが破棄されると、自動的「rollback」が実行される。**トランザクションに含まれるデータに対する、ありえる変更は失われるだろう。**
179179
180180### なぜ TSQLQuery.RecordCount はいつも10を返すのか
181181
182182データセットのレコードを数えるためには、「.RecordCount」を用いること。しかし、「.RecordCount」はサーバからすでに送り出されたレコードの数を示している。性能上の理由により、SqlDBはデフォルトではTSQLQueryを開いたときすべてのレコードを読まず、最初の10を読む。11番目のレコードを読むときのみに次の10レコードがアクセスされる、など。「.Last」を用いると、すべてのレコードが読み込まれる。 サーバにあるレコードの真の数を知りたいときは、初めに「.Last」、そして「.RecordCount」を呼ぶことができる。 また別のやり方もある。サーバによって返されるレコードの数は「.PacketRecords」プロパティにセットされる。そのデフォルト値は10、これを-1に設定すると、一度にすべてのレコードが読み込まれる。
183183
184184現在の安定版のFPCでは、.RecordCountはフィルタを考慮に入れていない。即ち、それはフィルタをかけられない総和となる。
185185
186186もしレコードの正確な数を必要としているのであれば、ほかのSQLクエリをクエリの中で用いてレコードの数を直接問い合わせるのがしばしばより良い考えであるが、その間に他のトランザクションがレコードの数を変えたかもしれないので、同じトランザクションの中でそれをしなければならないだろう。
187187
188188### Lazarus
189189
190190Lazarusはフォーム上にTDatasetからのデータを示すために様々なコンポーネントを持っている。正しいTSQLConnection、TSQLTransaction、TSQLQueryコンポーネントをフォームに置き、そしてそれらを適切に接続、設定すること。加えて、TDatasourceが必要となり、「TDatasource.Dataset」プロパティを、用いるTSQLQueryコンポーネントに設定すること。 (**注意**「TSQLQuery.Datasource」プロパティを、用いるTDatasourceコンポーネントに設定しないこと。「TSQLQuery.Datasource」プロパティはmaster-detailテーブルのみに使われる - [[MasterDetail]]参照)。結果としてTDGridをフォームに配置して、前に加えたTDatasourceをグリッドのプロパティに設定するることになる。
191191
192192これらすべてが機能していることを見るために、TSQLConnectionの「Connected」プロパティをLazarus IDEで「True」に設定すること。IDEはデータベースサーバへ直ちに接続を試みるだろう。もし、これが動いたら、「TSQLQuery.Active」プロパティを「True」に設定できる。もしすべてが正しいなら、IDEの中に、直ちにテーブルからのすべてのデータがみられるだろう。
193193
194194## どのようにしてテーブルのデータを変更するのか
195195
196196レコード内のデータを変更するための一般的なプロセスは、TSQLQuery を取得して変更するレコードを検索し、そこで変更を加えてデータベースにプッシュし直すことである。[[TDataSet]] (TSQLQuery の派生元) は編集モードに設定する必要がある。 編集モードに入るには、「.Edit」、「.Insert」、または「.Append」メソッドを呼び出す。 現在のレコードを変更するには、「.Edit」メソッドを使用する。現在のレコードを変更するには、「.Edit」メソッドを使用する。 現在のレコードの前に新しいレコードを挿入するには、「.Insert」を使用する。 テーブルの最後に新しいレコードを挿入するには、「.Append」を使用する。編集モードでは、「フィールド」プロパティを通じてフィールド値を変更できる。「Post」を使用して新しいデータを検証する。データが有効な場合は、編集モードが終了する。たとえば「.Next」を使用して別のレコードに移動し、データセットが編集モードの場合、最初に「.Post」が呼び出される。「.Cancel」 を使用すると、最後の 「.Post」 呼び出し以降に加えたすべての変更が破棄され、編集モードが終了します。
197197
198198``` pascal
199199Query.Edit;
200200Query.FieldByName('NAME').AsString := 'Edited name';
201201Query.Post;
202202```
203203
204204上記はまだ完全な話ではない。 TSQLQuery は、バッファされた更新を利用する TBufDataset から派生している。バッファ更新とは、「Post」を呼び出した後、データセット内の変更がすぐに表示されるが、データベース サーバーには送信されないことを意味する。実際に起こるのは、変更が変更ログに保持されることである。「.ApplyUpdates」メソッドが呼び出されると、変更ログ内のすべての変更がデータベースに送信される。そうして初めて、データベース サーバーは変更を認識する。変更は、TSQLTransaction のトランザクション内でサーバーに送信される。「ApplyUpdates」の前にトランザクションを正しく設定すること。更新を適用した後、コミットを実行して変更をデータベース サーバーに保存する必要がある。
205205
206206以下は、テーブル内のデータを変更し、変更をサーバーに送信し、トランザクションをコミットする例である。 繰り返すが、エラーチェックがない。これは非常に芳しくない!
207207
208208``` pascal
209209Program EditData;
210210{$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
211211uses
212212 db, sqldb, sqlite3conn;
213213var
214214 AConnection : TSQLConnection;
215215 ATransaction : TSQLTransaction;
216216 Query : TSQLQuery;
217217
218218begin
219219 AConnection := TSQLite3Connection.Create(nil);
220220 ATransaction := TSQLTransaction.Create(AConnection);
221221 AConnection.Transaction := ATransaction;
222222 AConnection.DatabaseName := 'test_dbase';
223223 Query := TSQLQuery.Create(nil);
224224 Query.DataBase := AConnection;
225225 Query.SQL.Text := 'select * from tblNames where ID = 2';
226226 Query.Open;
227227 Query.Edit;
228228 Query.FieldByName('NAME').AsString := 'Name Number 2';
229229 Query.Post;
230230 Query.UpdateMode := upWhereAll; // dbで定義されている
231231 Query.ApplyUpdates;
232232 ATransaction.Commit;
233233 Query.Free;
234234 ATransaction.Free;
235235 AConnection.Free;
236236end.
237237```
238238
239239実際の作業は、変更するレコードを識別する SQL ステートメント「select * from tblNames where ID = 2」から始まる。 「where ID = 2」ビットを省略すると、TSQLQuery は明らかに ID (および他の整数フィールド?) を 1 に設定する。そのため、ID=1 の行のみで動作する。「UpdateMode」については、引き続き読み進められたい。
240240
241241## どのようにして SqlDB はデータベースサーバに変更を送るのか
242242
243243#どのようにしてテーブルのデータを変更するのかのコード例で以下が、何をしているのか説明なしに記載されている。
244244
245245``` pascal
246246Query.UpdateMode := upWhereAll;
247247```
248248
249249その行が何をしているのかを知る最良の方法は、その行を省略することだ。 このステートメントを省略し、この Howto に正確に従った場合、次のエラー メッセージが表示される:
250250
251-`No update query specified and failed to generate one. (No fields for inclusion in where statement found)`
251+```text
252+No update query specified and failed to generate one. (No fields for inclusion in where statement found)
253+```
252254
253255何が問題なのかを理解するには、変更がどのようにデータベース サーバーに送信されるかを理解する必要がある。SQL サーバー内のデータを取得する唯一の方法は、SQL クエリを実行することだ。SQL には、レコードを操作する 3 つの異なる方法に対応する 3 種類のクエリがある。新しいレコードの作成、レコードの変更または削除には、それぞれ、insert、update、delete ステートメントが実行される。アップデートステートメントは以下のようなものだろう:
254256
255257``` sql
256258update TBLNAMES set NAME='Edited name' where ID=1;
257259```
258260
259261変更をデータベース サーバーに送信するには、Sqldb は更新クエリを組み立てる必要がある。クエリを組み立てるには、以下の3つが必要である:
260262
261263テーブルの名前
262264テーブル名は選択クエリの解析から取得されるが、これは常に機能するとは限らない。
263265
264266`UPDATE` もしくは `INSERT` 句
265267これらには、変更する必要があるフィールドが含まれている。
266268
267269`WHERE` 句
268270これには、どのレコードを変更する必要があるかを決定するフィールドが含まれる。
269271
270272すべてのフィールド(*Field*の各*TField*) には ProviderFlags プロパティがある。*ProviderFlags* に **pfInUpdate** があるフィールドのみが、クエリの更新または挿入条件で使用される。デフォルトではすべてのフィールドの *ProviderFlags* プロパティに *pfInUpdate* が設定されている。`WHERE` 句でどのフィールドが使用されるかは、クエリの *UpdateMode* プロパティとフィールドの *ProviderFlags* プロパティによって異なる。「ProviderFlags」に「pfInkey」が含まれるフィールドは、常に `WHERE` 句で使用されます。フィールドがテーブルの主キーの一部であり、「TSQLQuery.UsePrimaryKeyAsKey」が「True」を返す場合、フィールドには自動的に「pfInKey」フラグが設定されます。クエリの *UpdateMode* のデフォルト値は *upWhereKeyOnly* である。この更新モードでは、*ProviderFlags* プロパティに *pfInkey* を持つフィールドのみが `WHERE` 句で使用される。どのフィールドにも *pfInKey* フラグが設定されていない場合、`WHERE` 句に使用できるフィールドはなく、このセクションの冒頭のエラー メッセージが返される。
271273
272274この問題は以下で解決される:
273275
274276- テーブルにプライマリキーを加え、*TSQLQuery.UsePrimaryKeyAsKey* を 「True」に設定するか、
275277- コードの中で*pfInkey*フラグを1つ、またはそれ以上のフィールドに設定する。
276278
277279**UpdateMode** プロパティは、さらに 2 つの可能な値を認識します。 「upWhereAll」を使用すると、「pfInWhere」フラグが `WHERE` 句に設定されているすべてのフィールドを追加できる。**UpdateMode**プロパティは2つの可能な値を知っている。デフォルトでは、すべてのフィールドにこのフラグが設定されている。「upWhereChanged」は 「pfInWhere」フラグセットを持つフィールド **および** 現在のレコードで、変更されたフィールドに用いることができる。
278280
279281## エラーの取り扱い方
280282
281283実行時エラーは避けられず、ディスクがいっぱいになる、必要なライブラリやヘルパー アプリケーションが利用できない、等の問題が発生する可能性があるため、それを許容する必要がある。 FPC は実行時エラーを非常に適切に検出して処理する。通常、何が問題だったのかについて簡潔かつ合理的な説明が得られるが、多くの理由でエラーを監視し、取り扱うことを望むだろう -
282284
283285- おそらく、問題の最初の兆候が現れた時点でプログラムが終了することは望ましくないだろう。
284286- 続行する場合は、問題領域に割り当てられたメモリが回復されることを確認すること。メモリ リークは発生しないようにする。
285287- ただし、さらに詳しく説明する場合は、状況に応じた説明をユーザーに提供しよう。
286288
287289次のコードは上記の例に基づいているが、今回は重要な場所でのエラーをチェックをする。 鍵はtry...finally...end と try...except...end ブロックである。SQLite3をアンインストールして、test_dbaseのあったところにダミーファイルを置くなどして、これをテストすることができる。
288290
289291``` pascal
290292program DemoDBaseWithErrors;
291293{$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
292294uses
293295 DB, Sysutils, sqldb, sqlite3conn;
294296var
295297 Connect : TSQLite3Connection;
296298 Trans : TSQLTransaction;
297299
298300procedure WriteTable (Command : string);
299301begin
300302 Connect.ExecuteDirect(Command);
301303 Trans.Commit;
302304end;
303305
304306procedure ReadTable ();
305307var
306308 Query : TSQLQuery;
307309 Count : smallint;
308310begin
309311 Count := 0;
310312 try
311313 Query := TSQLQuery.Create(nil);
312314 Query.DataBase := Connect;
313315 Query.SQL.Text:= 'select * from tblNames';
314316 Query.Open; // これは接続を開始する
315317 while not Query.EOF do begin
316318 writeln('ID: ', Query.FieldByName('ID').AsInteger, ' Name: ' +
317319 Query.FieldByName('Name').AsString);
318320 Query.Next;
319321 Count := Count + 1;
320322 end;
321323 finally
322324 Query.Close;
323325 Query.Free;
324326 end;
325327 writeln('Found a total of ' + InttoStr(Count) + ' lines.');
326328end;
327329
328330procedure FatalError(ClassName, Message, Suggestion : string);
329331begin
330332 writeln(ClassName);
331333 writeln(Message);
332334 writeln(Suggestion);
333335 Connect.Close; // 解放することを心配するのはばかげているかもしれない。
334336 Trans.free; // halt()を呼ぶつもりだが
335337 Connect.Free; // これはデモだから、いいか ?
336338 halt();
337339end;
338340
339341begin
340342 Connect := TSQLite3Connection.Create(nil);
341343 Trans := TSQLTransaction.Create(Connect);
342344 Connect.Transaction := Trans;
343345 Connect.DatabaseName := 'test_dbase';
344346 try
345347 if not fileexists(Connect.DatabaseName) then begin
346348 Connect.Open; // もし例えばSQLiteがインストールされていないときにEInOutErrorをあげる
347349 Trans.StartTransaction;
348350 WriteTable('create table TBLNAMES (ID integer Primary Key, NAME varchar(40));');
349351 Trans.Commit;
350352 end;
351353 Connect.open;
352354 Trans.StartTransaction;
353355 WriteTable('insert into TBLNAMES (NAME) values (''AName1'');');
354356 WriteTable('insert into TBLNAMES (NAME) values (''AName2'');');
355357 except
356358 on E : EDatabaseError do
357359 FatalError(E.ClassName, E.Message, 'Does the file contain the correct database ?');
358360 on E : EInOutError do
359361 FatalError(E.ClassName, E.Message, 'Have you installed SQLite (and dev package)?');
360362 on E : Exception do
361363 FatalError(E.ClassName, E.Message, 'Something really really bad happened.');
362364 end;
363365 ReadTable();
364366 Connect.Close;
365367 Trans.Free;
366368 Connect.Free;
367369end.
368370```
369371
370372## どのようにしてTSQLQuery を用いてクエリを実行するのか
371373
372374次にデータセット(#どのようにしてテーブルからデータを取得するのか参照)を返すステートメントである。SQLはデータを返さないステートメントを持っている。例えば、`INSERT`、`UPDATE`、`DELETE`ステートメントはデータを返さない。これらは*TSQLConnection.ExecuteDirect*を用いることで実行できるが、TSQLQueryもまた用いることができる。もし、*TSQLQuery.Open* の代わりに *TSQLQuery.ExecSQL* を使って、データが変えることを期待しないなら、先に述べたように、SQLステートメントによりデータセットを開くために、*TSQLQuery.Open* を用いること。
373375
374376以下は TSQLQuery を用いてテーブルを作り、2つのレコードを作るプロシージャである。
375377
376378``` pascal
377379procedure CreateTable;
378380
379381var
380382 Query : TSQLQuery;
381383
382384begin
383385 Query := TSQLQuery.Create(nil);
384386 try
385387 Query.Database := AConnection;
386388
387389 Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
388390 Query.ExecSQL;
389391
390392 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (1,''Name1'');';
391393 Query.ExecSQL;
392394
393395 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (2,''Name2'');';
394396 Query.ExecSQL;
395397 finally
396398 Query.Free;
397399 end;
398400end;
399401```
400402
401403## どのようにしてクエリの中でパラメータを用いるのか
402404
403405In the code example of #どのようにしてTSQLQuery を用いてクエリを実行するのかのコード例の中で同じクエリが2回使われており、挿入される値が異なるだけである。これを行うより良い方法は、クエリの中でパラメータを用いることである。
404406
405407クエリ内のパラメータの構文はデータベース システムごとに異なるが、その違いは TSQLQuery によって処理される。
406408
407409クエリ内の値をコロンに置き換え、その後に使用するパラメータの名前を続ける。例えば:
408410
409411``` pascal
410412Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';
411413```
412414
413415このクエリは2つのパラメータ: 「ID」と「NAME」を作る。
414416
415417パラメーターを決定するために、*TSQLQuery.SQL* のテキストが割り当てられるか変更される時点でクエリが解析される。既存のパラメータはすべて削除され、新しいパラメータが「TSQLQuery.Params」プロパティに追加される。パラメーターに値を割り当てることは、データセット内のフィールドに値を割り当てることと似ている:
416418
417419``` pascal
418420Query.Params.ParamByName('Name').AsString := 'Name1';
419421```
420422
421423クエリからは、どのような種類のデータをパラメータに保存する必要があるかを知ることはできない。パラメータのデータ型は、パラメータに値が最初に割り当てられた時点で決定される。「.AsString」を使用して値を割り当てると、パラメータにはデータ型「ftString」が割り当てられる。'DataType' プロパティを設定することで、データ型を直接決定できる。不正なデータ型がパラメータに割り当てられている場合、クエリを開いたり実行したりするときに問題が発生する。 データ型についてのさらなる情報は [[Database field type]] を参照されたい。
422424
423425### Select クエリ
424426
425427パラメーターを使用した選択クエリの例は、次のように変更することになる:
426428
427429``` pascal
428430 Query.SQL.Text := 'select ID,NAME from TBLNAMES where NAME = '''+Edit1.Text+''' ORDER BY NAME ';
429431```
430432
431433をこのように:
432434
433435``` pascal
434436 Query.SQL.Text := 'select ID,NAME from TBLNAMES where NAME = :NAMEPARAM ORDER BY NAME ';
435437 Query.Params.ParamByName('NAMEPARAM').AsString := Edit1.Text;
436438```
437439
438440### 実装例
439441
440442以下の例は1つ前の例と同じテーブルを生成するが、パラメータが用いられている:
441443
442444``` pascal
443445procedure CreateTableUsingParameters;
444446
445447var
446448 Query : TSQLQuery;
447449
448450begin
449451 Query := TSQLQuery.Create(nil);
450452 try
451453 Query.Database := AConnection;
452454
453455 Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
454456 Query.ExecSQL;
455457
456458 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';
457459 Query.Prepare;
458460
459461 Query.Params.ParamByName('ID').AsInteger := 1;
460462 Query.Params.ParamByName('NAME').AsString := 'Name1';
461463 Query.ExecSQL;
462464
463465 Query.Params.ParamByName('ID').AsInteger := 2;
464466 Query.Params.ParamByName('NAME').AsString := 'Name2';
465467 Query.ExecSQL;
466468
467469 //Query.UnPrepare; // これを呼ぶ必要はない; Query.Closeによって呼ばれるはずである
468470 Query.Close;
469471 finally
470472 Query.Free;
471473 end;
472474end;
473475```
474476
475477この例はパラメータを用いないコードよりもコード量が多くなることに気を付けること。ではパラメータの用途は何であろうか?
476478
477479速度が1つの理由である。このパラメータを用いた例はより実行が速い、何故なら(.Prepareステートメントもしくは最初の実行で)データベースサーバはクエリを1回のみパースするからである。
478480
479481またプリペアードステートメントを用いる別の理由は、[SQL-インジェクション](http://en.wikipedia.org/wiki/SQL_injection)を防ぐことになる ([[Secure programming|Secure programmingも]]参照のこと)。
480482
481483最後に、ある例ではコーディングを簡素化するためである。
482484
483485## 問題解決法: TSQLConnection ログ
484486
485487TSQLConnection の動作をログに記録させることができる。 これは、Lazarus プログラムがデータベースに送信する内容を正確に確認したり、データベース コンポーネント自体をデバッグしたり、クエリを最適化したりする場合に便利である。 注意: 準備されたステートメント/パラメータ化されたクエリ (上記のセクションを参照) を使用する場合、パラメータは多くの場合、TSQLConnection の派生 (TIBConnection など) によってバイナリで送信されるため、ログに記録された SQL をコピーしてデータベース クエリ ツールに貼り付けるだけではすまない。いずれにせよ、接続ログにより、プログラムの動作について多くの洞察が得られる。
486488
487489これに代わるものとして:
488490
4894911. デバッグを有効にして FPC (および Lazarus) をビルドした場合は、デバッガーを使用してデータベース コードをステップ実行できます。
4904922. ODBC ドライバーを使用している場合 (少なくとも Windows では)、ODBC コントロール パネルでトレースログ出力を有効にすることができる。
4914933. 多くのデータベースでは、特定の IP アドレス/接続から送信されるすべてのステートメントを監視できる。
492494
493495TSQLConnection ログを使用する場合は、2 つのことが必要である:
494496
4954971. TSQLConnection がログに記録する必要があるイベント タイプを指定する。
4964982. イベントを受信して処理する関数に TSQLConnection を指定する (イベントをファイルに記録したり、画面に出力したりするなど)。
497499
498500この関数は TDBLogNotifyEvent 型である必要があるため (sqldb.pp を参照)、次の署名が必要である:
499501
500502``` pascal
501503TDBLogNotifyEvent = Procedure (Sender : TSQLConnection; EventType : TDBEventType; Const Msg : String) of object;
502504```
503505
504506### FPC (もしくは: 手動的方法)
505507
506508コード スニペットでこれを説明できる:
507509
508510``` pascal
509511uses
510512...
511513TSQLConnection, //もしくはTIBConnection、TMSSQLConnection といった派生オブジェクト
512514...
513515var
514516type
515517 TMyApplication = class(TCustomApplication); //これが接続を用いるアプリケーション
516518...
517519 private
518520 // この例はこの文字列リストにログを格納する:
519521 FConnectionLog: TStringList;
520522...
521523 protected
522524 // このプロシージャは接続によるログのイベントを受け取るだろう:
523525 procedure GetLogEvent(Sender: TSQLConnection; EventType: TDBEventType; Const Msg : String);
524526...
525527 procedure TMyApplication.GetLogEvent(Sender: TSQLConnection;
526528 EventType: TDBEventType; const Msg: String);
527529 // TSQLConnectionによってプロシージャが呼ばれ、FConnectionLogに
528530 // 受け取ったメッセージログが保存される
529531 var
530532 Source: string;
531533 begin
532534 // 見た目よく右揃え
533535 case EventType of
534536 detCustom: Source:='Custom: ';
535537 detPrepare: Source:='Prepare: ';
536538 detExecute: Source:='Execute: ';
537539 detFetch: Source:='Fetch: ';
538540 detCommit: Source:='Commit: ';
539541 detRollBack: Source:='Rollback:';
540542 else Source:='Unknown event. Please fix program code.';
541543 end;
542544 FConnectionLog.Add(Source + ' ' + Msg);
543545 end;
544546
545547...
546548 // 何をログに残したいか TSQLConnection に知らせる必要がある:
547549 FConnection.LogEvents:=LogAllEvents; //= [detCustom, detPrepare, detExecute, detFetch, detCommit, detRollBack]
548550 // ... そして、どのプロシージャに向けて、接続がイベントを送るべきか:
549551 FConnection.OnLog:=@Self.GetLogEvent;
550552...
551553 // これで接続を使用できるようになり、FConnectionLog 文字列リストにログ メッセージが表示される
552554```
553555
554556代わりに TSQLConnection の GlobalDBLogHook を使用して、複数の接続からすべてをログに記録することもできる。
555557
556558### Lazarus (もしくは: 速い方法)
557559
558560最後に、上記の説明は、冒頭で示した FPC の方法である。 Lazarus を使用している場合、より簡単な方法は、TSQLConnection の OnLog イベントにイベント ハンドラーを割り当てることである。
559561
560562## 以下も参照のこと
561563
562564- [[Working With TSQLQuery]]
563565
564566---
565567*From the Free Pascal wiki, page "SqlDBHowto/ja", as it stood on 2024-04-01 after a change by Ariben, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history