Difference between revisions of "SqlDBHowto/ja"

From Free Pascal wiki
Jump to navigationJump to search
(Created page with "{{Japanese Menu}} {{SqlDBHowto}} {{Infobox databases}} This text is setup as a 'how-to'. I want to answer a number of questions one by one, and explain how you can use the v...")
 
 
(47 intermediate revisions by the same user not shown)
Line 1: Line 1:
 +
{{SqlDBHowto}}
 +
{{Infobox databases/ja}}
 
{{Japanese Menu}}
 
{{Japanese Menu}}
{{SqlDBHowto}}
 
  
{{Infobox databases}}
 
  
This text is setup as a 'how-to'. I want to answer a number of questions one by one, and explain how you can use the various classes. All those questions are put one after the other and form a sort of tutorial.
+
このテキストは「how-to」として作られている。数多くの質問に1つ、1つ答え、どのように様々なクラスを用いるのか説明したい。これらすべての質問は次から次へと沸き起こり、ある種のチュートリアルとなった。
  
I will try to word it in such a way that the text can be used for Lazarus as well as Free Pascal. However, the examples are for FreePascal (i.e. they are console applications.)
+
このテキストがFree Pascal同様にLazarusでも用いられるように書くつもりだが、例はFree Pascal(即ち、コンソールアプリケーション)に向けてある。
  
== Where can I find official documentation? ==
+
== どこで公式のドキュメンテーションが見つかるか ==
  
Please see the official documentation at [http://www.freepascal.org/docs-html/fcl/sqldb/index.html SQLDB documentation].
+
公式ドキュメンテーションはここを参照されたい[http://www.freepascal.org/docs-html/fcl/sqldb/index.html SQLDBドキュメンテーション]
  
== How to connect to a database server? ==
+
== どのようにしてデータベースサーバに接続するか ==
  
SqlDB doesn't connect to a database server directly but uses a client that corresponds to the used database server. SqlDB sends the commands to the client library; the client library connects to the database and and transfers the commands. This means that a client library must be installed on the computer to make a connection to a database. Under Windows a client is usually a .dll, under Linux an .so and under OS/X a .dylib.
+
SqlDBは直接データベースサーバには接続せず、用いられているデータベースサーバに対応するクライアントを用いる。SqlDBはクライアントライブラリにコマンドを送り、クライアントライブラリがデータベースサーバに接続しコマンドを送る。これはデータベースと接続を持つためにクライアントライブラリがインストールされていなければならないことを意味する。Windowsではクライアントライブラリは通常、.dll、Linux では.so、OS/Xでは.dylibである。
  
When the client library is installed properly you can connect to a database server using a TSQLConnection component. Various TSQLConnection components are available for different database servers (see [[SQLdb_Package]]):
+
クライアントライブラリが適切にインストールされているときには、TSQLConnectionコンポーネントを用いてデータベースサーバに接続できる。様々なTSQLConnectionコンポーネントが異なるデータベースサーバに対して利用可能である([[SQLdb_Package]]を参照):
  
 
* Firebird/Interbase: [[TIBConnection]]
 
* Firebird/Interbase: [[TIBConnection]]
* MS SQL Server: [[TMSSQLConnection]] (available since FPC 2.6.1)
+
* MS SQL Server: [[TMSSQLConnection]] (FPC 2.6.1から利用可能)
 
* MySQL v4.0: [[TMySQL40Connection]]  
 
* MySQL v4.0: [[TMySQL40Connection]]  
 
* MySQL v4.1: [[TMySQL41Connection]]  
 
* MySQL v4.1: [[TMySQL41Connection]]  
 
* MySQL v5.0: [[TMySQL50Connection]]  
 
* MySQL v5.0: [[TMySQL50Connection]]  
* MySQL v5.1: [[TMySQL51Connection]] (available since FPC version 2.5.1
+
* MySQL v5.1: [[TMySQL51Connection]] (FPC version 2.5.1から利用可能)
* MySQL v5.5: [[TMySQL55Connection]] (available since Lazarus 1.0.8/FPC version 2.6.2
+
* MySQL v5.5: [[TMySQL55Connection]] (Lazarus 1.0.8/FPC version 2.6.2から利用可能)
* MySQL v5.6: [[TMySQL56Connection]] (available in Lazarus 1.2.4/FPC version 2.6.4
+
* MySQL v5.6: [[TMySQL56Connection]] (Lazarus 1.2.4/FPC version 2.6.4から利用可能)
* ODBC: [[TODBCConnection]] (see [[ODBCConn#TODBCConnection]])
+
* MySQL v5.7: [[TMySQL57Connection]] (訳注:いつから?)
* Oracle: [[TOracleConnection]] (see [[Oracle]])
+
* ODBC: [[TODBCConnection]] ([[ODBCConn#TODBCConnection]]を参照)
* PostgreSQL: [[TPQConnection]] (see [[postgresql#SQLDB]])
+
* Oracle: [[TOracleConnection]] ([[Oracle]]を参照)
* Sqlite3: [[TSQLite3Connection]] (available since FPC version 2.2.2, see [[SQLite#Built-in_SQLDB]])  
+
* PostgreSQL: [[TPQConnection]] ([[postgresql#SQLDB]]を参照)
* Sybase ASE: [[TSybaseConnection]] (available since FPC 2.6.1, see [[Lazarus_Database_Overview#Lazarus_and_MSSQL.2FSybase|Lazarus and MSSQL/Sybase]])
+
* Sqlite3: [[TSQLite3Connection/ja]] (FPC version 2.2.2から利用可能、[[SQLite#Built-in_SQLDB]]を参照)  
 +
* Sybase ASE: [[TSybaseConnection]] (FPC 2.6.1から利用可能、[[Lazarus_Database_Overview#Lazarus_and_MSSQL.2FSybase|LazarusとMSSQL/Sybase]])
  
Note for MySQL - There are many differences between the client versions to the extent that the clients and connections cannot be interchanged. If a MySQL client library version 4.1 is installed, you have to use a TMySQL41Connection. This is not related to the MySQL server; using the MySQL 4.1 client library you can probably connect to a MySQL 5.0 server (see MySQL documentation regarding what combinations are supported).
+
MySQLに対する注意 - クライアントとコネクションが相互交換できないほどクライアントバージョン間に多くの相違がある。もしクライアントライブラリ バージョン 4.1がインストールされているとすると、TMySQL41Connectionを用いなければならない。これはMySQLサーバには関連しておらずMySQL 4.1クライアントライブラリを用いて、おそらくMySQL 5.0サーバに接続できる(どの組み合わせがサポートされているかはMySQLドキュメンテーションを参照されたい)
  
Although details differ for the various databases, in general you need to set four properties to connect to a database server:  
+
さまざまなデータベースで細部は異なるが、一般的にデータベースサーバに接続するために4つのプロパティを設定しなければならない:
  
* the server name or IP address
+
* サーバの名前とIPアドレス
* the name of the database
+
* データベースの名前
* the username
+
* ユーザー名
* the password
+
* パスワード
  
When these properties are set, you can create a connection with the 'open' method. If the connection fails, a EDatabaseError exception is thrown. Use the property 'connected' to test if a connection has been made with the database server. Use the 'close' method to end the connection with the server.
+
これらのプロパティが設定されると、サーバに「open」メソッドを作ることができる。もし接続に失敗するとEDatabaseError例外が生じる。データベースサーバに接続が確立しているかどうか確かめるために、「connected」プロパティを用いること。
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 77: Line 78:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
If an exception is thrown, read the error message carefully. It may be that the database server is not running, the user name or password are incorrect or the database name or IP address are typed incorrectly. If the error message states that the client library cannot be found, then check if the client is installed correctly. Often the error message states literally the name of the file looked for.
+
もし、エラーが発生したら、注意してエラーメッセージを読むこと。データベースサーバが稼働していない、ユーザー名もしくはパスワードが誤っている、データベース名またはIPアドレスが誤って入力されたかもしれない。もしエラーメッセージがクライアントライブラリが見つからないと言っているなら、クライアントライブラリが正しくインストールされているか確かめること。しばしばエラーメッセージは探しているファイル名を文字通り言う。
  
== How to execute direct queries/make a table? ==
+
== どのようにして直接クエリを実行/テーブルを作るのか ==
  
SqlDB - the name says it all - only works with database server that make use of SQL. SQL stands for 'Structured Query Language' SQL is a language developed to allow working with relational databases. Virtually every database system has its own dialect, but a large number of SQL statements are the same for all database systems.
+
SqlDB - 文字通り名前がすべてを体現している - はSQLを用いて唯一、データベースサーバとやり取りをする。SQL は 「Structured Query Language」を意味する。 SQLはリレーショナルデータベースとやり取りすることを可能にするために開発された言語である。実際にはすべてのデータベースシステムはそれぞれ方言を持っているが、SQLステートメントの多くはすべてのデータベースシステムで共通している。
  
In FPC, there is a difference between:
+
FPCでは、以下の違いがある:
* SQL statements that return information (a dataset). For this, you have to use the TSQLQuery component; see [[#How to read data from a table?]].
+
* 情報(データセット)を返すSQLステートメント。このため、TSQLQueryコンポーネントを用いなければならない; [[#How to read data from a table?]]を参照のこと。
* statements that do not return information but do something else, e.g. update data. For this, you may also use the 'ExecuteDirect' method of a TSQLConnection. (You can also use this if you get a dataset back but are not interested in the results, e.g. in a selectable stored procedure).
+
* 情報を返さずに、変わって何かを行うステートメント、例えば、データを更新する。このため、またTSQLConnectionの「ExecuteDirect」メソッドを使うかもしれない(データセットを引き戻すためにこれを使うこともできるが、結果に興味はない、例えばセレクタブルストアドプロシージャ)
  
Most database system execute SQL statements within a transaction. If you want changes made within a transaction available in other transactions, or have those changes available even after closing the transaction(!), then you have to 'commit' the transaction.
+
大半のデータベースシステムはトランザクションでSQLステートメントを実行する。もし、他のトランザクションの中で利用できるトランザクションの中で行われる変更を望む、もしくはトランザクションが閉じた後でそれらの変更する(!)ならば、トランザクションを「commit」しなければならない。
  
To support transactions Sqldb contains the TSQLTransaction component. A SQL statement that is executed by Sqldb must always be executed within a transaction, even if the database system does not support transactions. Also, there are database systems that do support transaction for which TSQLConnection does not (yet) support transaction. Even then, you must use the TSQLTransaction component.
+
トランザクションをサポートするために、SqlDBはTSQLTransactionコンポーネントを含んでいる。SqlDBによって実行されるSQLステートメントはデータベースシステムが、トランザクションをサポートしていなくとも、常にトランザクションの最中に行われる必要がある。同様に、TSQLTransactionがいまだにサポートしていないトランザクションをサポートしているデータベースシステムも存在する。その時ですらTSQLTransactionコンポーネントを使わなければならない。
  
To use <tt>TSQLConnection.ExecuteDirect</tt> to execute a SQL statement you must specify which 'Transaction' must be used. In turn, to use TSQLTransaction you must specify which TSQLConnection component must be used.
+
SQLステートメントを実行するために<tt>TSQLConnection.ExecuteDirect</tt>を使うには、どの「Transaction」が使われなければならないかを明示しなければならない。逆に、TSQLTransactionを使うためには、どのTSQLTransactionコンポーネントが使われるかを明示しなければならない。
  
The following example creates a table 'TBLNAMES' with fields 'NAME' and 'ID' and inserts two records. This time using SQLite. The used SQL statements are not explained. For more information about the SQL statements, their use and syntax, please refer to the database system documentation. Note that this example does not attempt to catch any errors, thats a bad thing! Look into [https://www.freepascal.org/docs-html/current/ref/refch17.html#x226-24800017 Exceptions].
+
以下の例は「NAME」と「ID」フィールドを持つ「TBLNAMES」を生成し2つのレコードを挿入する。今回はSQLiteを用いる。使われるSQLステートメントは説明されない。SQLステートメント、その使用と文法に関しては、データベースシステムのドキュメンテーションを参照されたい。
 +
この例ではいかなるエラーの捕捉も試みないことに注意すること、それは悪いことである! [https://www.freepascal.org/docs-html/current/ref/refch17.html#x226-24800017 例外]を調べていただきたい。
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 126: Line 128:
 
end. </syntaxhighlight>
 
end. </syntaxhighlight>
  
== How to read data from a table? ==
+
== どのようにしてテーブルからデータを取得するのか ==
Use the TSQLQuery component to read data from a table. A TSQLQuery component must be connected to a TSQLConnection component and a TSQLTransaction component to do its work. Setting the TSQLConnection and TSQLTransaction is discussed in [[#How to connect to a database server? ]] and [[#How to execute direct queries/make a table?]].
+
テーブルからデータを読み込むときにはTSQLQueryを用いること。TSQLQueryコンポーネントはその仕事をするために、TSQLConnectionコンポーネントとTSQLTransactionコンポーネントに接続されなければない。TSQLConnectionとTSQLTransactionの設定は[[#How to connect to a database server? ]][[#How to execute direct queries/make a table?]]で議論する。
  
When the TSQLConnection, TSQLTransaction and TSQLQuery are connected, then TSQLQuery needs to be further configured to work.  TSQLQuery has a 'SQL' property containing a TStrings object. The 'SQL' property contains a SQL statement that must be executed. If all data from a table <tt>tablename</tt> must be read, then set the 'SQL' property to:
+
TSQLConnectionの時、TSQLTransactionとTSQLQueryが接続される、そしてTSQLQueryは動作するためにさらに設定される。TSQLQueryはTStringsオブジェクトを含む「SQL」プロパティを持つ。「SQL」プロパティは実行されるべきSQLステートメントを含んでいる。もしテーブル<tt>tablename</tt>のすべてのデータを読まなければならないときには、「SQL」プロパティを以下のように設定する:
<syntaxhighlight lang="sql">'SELECT * FROM tablename;'</syntaxhighlight>.
+
<syntaxhighlight lang="sql">'SELECT * FROM tablename;'</syntaxhighlight>
  
Use 'open' to read the table from the server and put the data in the TSQLQuery dataset. The data can be accessed through TSQLQuery until the query is closed using 'close'.
+
サーバからテーブルを読み込み、TSQLQueryデータセットにデータを読み込むには「open」を使うこと。データは「close」を用いてクエリが閉じられるまでTSQLQueryを通してアクセスできる。
  
TSQLQuery is a subclass of TDataset. TDataset has a 'Fields' collection that contains all columns of the table. The TDataset also keeps track of the current record. Use 'First', 'Next', 'Prior' and 'Last' to change the current record. 'Bof' returns 'True' if the first record is reached, and 'Eof' returns 'True' if the last record is reached. To read the value of a field in the current record, first find the right 'TField' object and then use 'AsString', 'AsInteger', etc.
+
TSQLQueryはTDatasetのサブクラスである。TDatasetはテーブルのすべてのカラムを含む「Field」コレクションを持っている。TDatasetはまた、現在のレコードを追跡する。現在のレコードを変更するために「First」、「Next」、「Prior」、「Last」を用いること。もし最初のレコードが届いたら「Bof」が「True」となり、もし最後のレコードに達したら、「Eof」が「True」になる。現在のレコードの値を読むためには初めに正しい「TField」オブジェクトを見つけ、そして「AsString」、「AsInteger」を用いること。
  
=== Example: reading data from a table ===
+
=== : テーブルからデータを取得する ===
Below is an example that displays all values of the table as it was made in CreateTable example above. Copy the test_database file into the ShowData working directory. Note, again, no error checking has taken place !
+
以下は上のCreateTable例で作られたテーブルのすべての値を表示する例である。test_databaseファイルをShowDataワーキングディレクトリにコピーすること。エラーチェックを行っていないことに再び注意すること!
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 173: Line 175:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
(The code above of course is not quite finished, it misses 'try...finally' blocks. However, the above code intends to show the database code and thus the finishing touches are left out.)
+
もちろん、上記のコードは未完成で、「try...finally」ブロックを欠く。しかし、上のコードはデータベースコードを示す意図のもので、そのため仕上げの一筆は省いてある。
Please note that 'TSQLTransaction.StartTransaction' is not used. This is not necessary. When TSQLQuery is opened, the SQL statement is executed and if no transaction is available then a transaction is automatically started. The programmer does not need to start the transaction explicitly.
+
「TSQLTransaction.StartTransaction」が使われてないことに注意してほしい。これは不要である。TSQLQueryが開かれるとき、SQLステートメントが実行され、もしトランザクションが利用できないときにはトランザクションが自動的に始まる。プログラマは明示的にトランザクションを開始させる必要はない。
The same applies for the connection maintained by TSQLConnection. The connection is opened as needed, the line 'Aconnection.Open' is not really required.
+
同様のことがTSQLConnectionによって維持されている接続にも適用される。接続は必要に応じて開かれ、「Aconnection.Open」は全く必要ない。もし、TSQLTransactionが破棄されると、自動的「rollback」が実行される。'''トランザクションに含まれるデータに対する、ありえる変更は失われるだろう。'''
If a TSQLTransaction is destroyed, an automatic 'rollback' will be executed. '''Possible changes to data contained in the transaction will be lost.'''
 
 
 
=== Why does TSQLQuery.RecordCount always return 10? ===
 
To count the records in a dataset, use '.RecordCount'. However, notice that '.RecordCount' shows the number of records that is already loaded from the server. For performance reasons, SqlDB does not read all records when opening TSQLQuery by default, only the first 10. Only when the eleventh record is accessed will the next set of 10 records be loaded, etc. Using '.Last', all records will be loaded.
 
  
When you want to know the real number of records on the server you can first call '.Last' and then call '.RecordCount'.  
+
=== なぜ TSQLQuery.RecordCount はいつも10を返すのか ===
 +
データセットのレコードを数えるためには、「.RecordCount」を用いること。しかし、「.RecordCount」はサーバからすでに送り出されたレコードの数を示している。性能上の理由により、SqlDBはデフォルトではTSQLQueryを開いたときすべてのレコードを読まず、最初の10を読む。11番目のレコードを読むときのみに次の10レコードがアクセスされる、など。「.Last」を用いると、すべてのレコードが読み込まれる。
 +
サーバにあるレコードの真の数を知りたいときは、初めに「.Last」、そして「.RecordCount」を呼ぶことができる。
 +
また別のやり方もある。サーバによって返されるレコードの数は「.PacketRecords」プロパティにセットされる。そのデフォルト値は10、これを-1に設定すると、一度にすべてのレコードが読み込まれる。
  
An alternative is available. The number of records returned by the server is set by the '.PacketRecords' property. The default value is 10; if you make it -1 then all records will be loaded at once.
+
現在の安定版のFPCでは、.RecordCountはフィルタを考慮に入れていない。即ち、それはフィルタをかけられない総和となる。
  
In current stable FPC, '.RecordCount' does not take filters into account, i.e. it shows the unfiltered total.
+
もしレコードの正確な数を必要としているのであれば、ほかのSQLクエリをクエリの中で用いてレコードの数を直接問い合わせるのがしばしばより良い考えであるが、その間に他のトランザクションがレコードの数を変えたかもしれないので、同じトランザクションの中でそれをしなければならないだろう。
  
If you need the exact number of records, it often is a better idea to directly query the number of records in a query using another SQL query, but you would have to do that in the same transaction, as other transactions may have changed the number of records in the meanwhile.
 
  
 
=== Lazarus ===
 
=== Lazarus ===
Lazarus has various components to show data from a TDataset on a form. Instead of a While-loop and Writeln statements as used above, you can use the components to show the data in a table. Place the right TSQLConnection, TSQLTransaction and TSQLQuery components on a form, then connect them and set them properly. In addition you will need a TDatasource; set the 'TDatasource.Dataset' property to the TSQLQuery component you used. ('''Note''' do not set the 'TSQLQuery.Datasource' property to the TDatasource compnent you used. The 'TSQLQuery.Datasource' property is used only in master-detail tables - see [[MasterDetail]]) Subsequently you may put a TDBGrid onto the form and set the 'Datasource' property of the grid to the TDatasource component you added before.
+
Lazarusはフォーム上にTDatasetからのデータを示すために様々なコンポーネントを持っている。正しいTSQLConnection、TSQLTransaction、TSQLQueryコンポーネントをフォームに置き、そしてそれらを適切に接続、設定すること。加えて、TDatasourceが必要となり、「TDatasource.Dataset」プロパティを、用いるTSQLQueryコンポーネントに設定すること。
 
+
('''注意'''「TSQLQuery.Datasource」プロパティを、用いるTDatasourceコンポーネントに設定しないこと。「TSQLQuery.Datasource」プロパティはmaster-detailテーブルのみに使われる - [[MasterDetail]]参照)。結果としてTDGridをフォームに配置して、前に加えたTDatasourceをグリッドのプロパティに設定するることになる。
To see if it all works, set the 'Connected' property of the TSQLConnection to 'True' in the Lazarus IDE. The IDE will try to connect to the database server immediately. If this works you can set the 'TSQLQuery.Active' property to 'True'. If everything is right, you will see - within the IDE - all data from the table immediately on the screen.
 
  
== How to change data in a table? ==
+
これらすべてが機能していることを見るために、TSQLConnectionの「Connected」プロパティをLazarus IDEで「True」に設定すること。IDEはデータベースサーバへ直ちに接続を試みるだろう。もし、これが動いたら、「TSQLQuery.Active」プロパティを「True」に設定できる。もしすべてが正しいなら、IDEの中に、直ちにテーブルからのすべてのデータがみられるだろう。
To change the data in a record (or records), the general process is get TSQLQuery to search for the records you wish to change, make the changes there and then push them back to the database. The [[TDataSet]] (from which TSQLQuery is derived) must be set to edit mode. To enter edit mode call the '.Edit', '.Insert' or '.Append' methods. Use the '.Edit' method to change the current record. Use '.Insert' to insert a new record before the current record. Use '.Append' to insert a new record at the end of the table. In edit mode you can change field values through the 'Fields' property. Use 'Post' to validate the new data, if the data is valid then the edit mode is left. If you move to another record - for example by using '.Next' - and the dataset is in edit mode, then first '.Post' is called. Use '.Cancel' to discard all changes you made since the last '.Post' call and leave the edit mode.
 
  
 +
== どのようにしてテーブルのデータを変更するのか ==
 +
レコード内のデータを変更するための一般的なプロセスは、TSQLQuery を取得して変更するレコードを検索し、そこで変更を加えてデータベースにプッシュし直すことである。[[TDataSet]] (TSQLQuery の派生元) は編集モードに設定する必要がある。 編集モードに入るには、「.Edit」、「.Insert」、または「.Append」メソッドを呼び出す。 現在のレコードを変更するには、「.Edit」メソッドを使用する。現在のレコードを変更するには、「.Edit」メソッドを使用する。 現在のレコードの前に新しいレコードを挿入するには、「.Insert」を使用する。 テーブルの最後に新しいレコードを挿入するには、「.Append」を使用する。編集モードでは、「フィールド」プロパティを通じてフィールド値を変更できる。「Post」を使用して新しいデータを検証する。データが有効な場合は、編集モードが終了する。たとえば「.Next」を使用して別のレコードに移動し、データセットが編集モードの場合、最初に「.Post」が呼び出される。「.Cancel」 を使用すると、最後の 「.Post」 呼び出し以降に加えたすべての変更が破棄され、編集モードが終了します。
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
Query.Edit;
 
Query.Edit;
 
Query.FieldByName('NAME').AsString := 'Edited name';
 
Query.FieldByName('NAME').AsString := 'Edited name';
Query.Post;</syntaxhighlight>
+
Query.Post;
 +
</syntaxhighlight>
 +
上記はまだ完全な話ではない。 TSQLQuery は、バッファされた更新を利用する TBufDataset から派生している。バッファ更新とは、「Post」を呼び出した後、データセット内の変更がすぐに表示されるが、データベース サーバーには送信されないことを意味する。実際に起こるのは、変更が変更ログに保持されることである。「.ApplyUpdates」メソッドが呼び出されると、変更ログ内のすべての変更がデータベースに送信される。そうして初めて、データベース サーバーは変更を認識する。変更は、TSQLTransaction のトランザクション内でサーバーに送信される。「ApplyUpdates」の前にトランザクションを正しく設定すること。更新を適用した後、コミットを実行して変更をデータベース サーバーに保存する必要がある。
  
The above is not the complete story yet. TSQLQuery is derived from TBufDataset which makes use of buffered updates. Buffered update means that after you called 'Post' the changes in the dataset are visible immediately, but they are not sent to the database server. What does happen is that the changes are maintained in a change log. When the '.ApplyUpdates' method is called, then all changes in the change log are sent to the database. Only then will database server know of the changes. The changes are sent to the server within a transaction of TSQLTransaction. Make sure to properly set the transaction before 'ApplyUpdates'. After applying the updates, a commit must be executed to save the changes on the database server.
+
以下は、テーブル内のデータを変更し、変更をサーバーに送信し、トランザクションをコミットする例である。 繰り返すが、エラーチェックがない。これは非常に芳しくない!
 
 
The below is an example of changing the data in a table, sending the changes to the server and committing the transaction. Again, no error checking, again, thats bad!
 
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 228: Line 228:
 
   Query.FieldByName('NAME').AsString := 'Name Number 2';
 
   Query.FieldByName('NAME').AsString := 'Name Number 2';
 
   Query.Post;
 
   Query.Post;
   Query.UpdateMode := upWhereAll;        // defined in db
+
   Query.UpdateMode := upWhereAll;        // dbで定義されている
 
   Query.ApplyUpdates;
 
   Query.ApplyUpdates;
 
   ATransaction.Commit;
 
   ATransaction.Commit;
Line 237: Line 237:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The actual works starts with the SQL statement "select * from tblNames where ID = 2" identifying the record (or records) you wish to change. If you leave out the "where ID = 2" bit, the TSQLQuery apparently sets ID (and other integer fields?) to 1. And therefore will operate on lines where ID=1 only. For a discussion of 'UpdateMode' continue reading.
+
実際の作業は、変更するレコードを識別する SQL ステートメント「select * from tblNames where ID = 2」から始まる。 「where ID = 2」ビットを省略すると、TSQLQuery は明らかに ID (および他の整数フィールド?) 1 に設定する。そのため、ID=1 の行のみで動作する。「UpdateMode」については、引き続き読み進められたい。
  
== How does SqlDB send the changes to the database server? ==
+
== どのようにして SqlDB はデータベースサーバに変更を送るのか ==
 +
[[#どのようにしてテーブルのデータを変更するのか]]のコード例で以下が、何をしているのか説明なしに記載されている。
 +
<syntaxhighlight lang=pascal>Query.UpdateMode := upWhereAll;</syntaxhighlight>
 +
その行が何をしているのかを知る最良の方法は、その行を省略することだ。 このステートメントを省略し、この Howto に正確に従った場合、次のエラー メッセージが表示される:
  
In the code example in [[#How to change data in a table?]], you will find the line
 
<syntaxhighlight lang=pascal>Query.UpdateMode := upWhereAll;</syntaxhighlight>
 
without explanation of what it does. The best way to find out what that line does is to leave it out. If you leave out the statement and the followed this howto precisely, then you will receive the following error message:
 
 
  No update query specified and failed to generate one. (No fields for inclusion in where statement found)
 
  No update query specified and failed to generate one. (No fields for inclusion in where statement found)
To understand what went wrong, you must understand how changes are sent to the database server. The only way to get data in a SQL server is by executing SQL queries. SQL has three types of queries for three different ways of manupulating a record. To create a new record, change or delete a record insert, update and delete statements are executed respectively. An update statement may be as follows:
+
 
 +
何が問題なのかを理解するには、変更がどのようにデータベース サーバーに送信されるかを理解する必要がある。SQL サーバー内のデータを取得する唯一の方法は、SQL クエリを実行することだ。SQL には、レコードを操作する 3 つの異なる方法に対応する 3 種類のクエリがある。新しいレコードの作成、レコードの変更または削除には、それぞれ、insert、update、delete ステートメントが実行される。アップデートステートメントは以下のようなものだろう:
 
<syntaxhighlight lang="sql">update TBLNAMES set NAME='Edited name' where ID=1;</syntaxhighlight>
 
<syntaxhighlight lang="sql">update TBLNAMES set NAME='Edited name' where ID=1;</syntaxhighlight>
To send a change to the database server, Sqldb must assemble an update query. To assemble the query, three things are needed:
+
変更をデータベース サーバーに送信するには、Sqldb は更新クエリを組み立てる必要がある。クエリを組み立てるには、以下の3つが必要である:
; The name of the table : The table name is retrieved from parsing the select query, although this doesn't always work.
+
; テーブルの名前 : テーブル名は選択クエリの解析から取得されるが、これは常に機能するとは限らない。
; <tt>UPDATE</tt> or <tt>INSERT</tt> clause : These contain the fields that must be changed.
+
; <tt>UPDATE</tt> もしくは <tt>INSERT</tt> : これらには、変更する必要があるフィールドが含まれている。
; <tt>WHERE</tt> clause : This contains the fields that determine which records should be changed.
+
; <tt>WHERE</tt> : これには、どのレコードを変更する必要があるかを決定するフィールドが含まれる。
  
Every field (each ''TField'' in ''Fields'') has a ProviderFlags property. Only fields with '''pfInUpdate''' in ''ProviderFlags'' will be used in the update or insert cluase of a query. By default all fields have ''pfInUpdate'' set in their ''ProviderFlags'' property.
+
すべてのフィールド(''Field''の各''TField'') には ProviderFlags プロパティがある。''ProviderFlags'' に '''pfInUpdate''' があるフィールドのみが、クエリの更新または挿入条件で使用される。デフォルトではすべてのフィールドの ''ProviderFlags'' プロパティに ''pfInUpdate'' が設定されている。<tt>WHERE</tt> 句でどのフィールドが使用されるかは、クエリの ''UpdateMode'' プロパティとフィールドの ''ProviderFlags'' プロパティによって異なる。「ProviderFlags」に「pfInkey」が含まれるフィールドは、常に <tt>WHERE</tt> 句で使用されます。フィールドがテーブルの主キーの一部であり、「TSQLQuery.UsePrimaryKeyAsKey」が「True」を返す場合、フィールドには自動的に「pfInKey」フラグが設定されます。クエリの ''UpdateMode'' のデフォルト値は ''upWhereKeyOnly'' である。この更新モードでは、''ProviderFlags'' プロパティに ''pfInkey'' を持つフィールドのみが <tt>WHERE</tt> 句で使用される。どのフィールドにも ''pfInKey'' フラグが設定されていない場合、<tt>WHERE</tt> 句に使用できるフィールドはなく、このセクションの冒頭のエラー メッセージが返される。
  
Which fields are used in the <tt>WHERE</tt> clause depends on the ''UpdateMode'' property of the query and the ''ProviderFlags'' property of the fields. Fields with ''pfInkey'' in their ''ProviderFlags'' are always used in the <tt>WHERE</tt> clause. A field will have the ''pfInKey'' flag set automatically if the field is part of the primary key of the table and 'TSQLQuery.UsePrimaryKeyAsKey' returns 'True'.
+
この問題は以下で解決される:
  
The default value for ''UpdateMode'' of the query is ''upWhereKeyOnly''. In this update mode only fields with ''pfInkey'' in their ''ProviderFlags'' property are used in the <tt>WHERE</tt> clause. If none of the fields have their ''pfInKey'' flag set, then no fields are available for the <tt>WHERE</tt> clause and the error message from the beginning of this section will be returned. You can solve the issue by:
+
* テーブルにプライマリキーを加え、''TSQLQuery.UsePrimaryKeyAsKey'' を 「True」に設定するか、
* Adding a primary key to the table and set ''TSQLQuery.UsePrimaryKeyAsKey'' to 'True', or
+
* コードの中で''pfInkey''フラグを1つ、またはそれ以上のフィールドに設定する。
* Setting the ''pfInkey'' flag for one or more fields in code.
 
  
The '''UpdateMode''' property knows two more possible values. 'upWhereAll' can be used to add all fields with the 'pfInWhere' flag set to the <tt>WHERE</tt> clause. By default all fields have this flag set. 'upWhereChanged' can be used to add only those fields that have the 'pfInWhere' flag set '''and''' that are changed in the current record.
+
'''UpdateMode''' プロパティは、さらに 2 つの可能な値を認識します。 「upWhereAll」を使用すると、「pfInWhere」フラグが <tt>WHERE</tt> 句に設定されているすべてのフィールドを追加できる。'''UpdateMode'''プロパティは2つの可能な値を知っている。デフォルトでは、すべてのフィールドにこのフラグが設定されている。「upWhereChanged」は 「pfInWhere」フラグセットを持つフィールド '''および''' 現在のレコードで、変更されたフィールドに用いることができる。
  
==How to handle Errors==
+
== エラーの取り扱い方 ==
Run time errors are unavoidable, disks may fill up, necessary libraries or helper apps may not be available, things go wrong and we need to allow for that.
+
実行時エラーは避けられず、ディスクがいっぱいになる、必要なライブラリやヘルパー アプリケーションが利用できない、等の問題が発生する可能性があるため、それを許容する必要がある。
The FPC detects and handles run time errors quite well. It usually gives you a concise and reasonable explanation of what went wrong. However, you will want
+
FPC は実行時エラーを非常に適切に検出して処理する。通常、何が問題だったのかについて簡潔かつ合理的な説明が得られるが、多くの理由でエラーを監視し、取り扱うことを望むだろう -
to monitor and handle errors yourself for a number of reasons -
+
* おそらく、問題の最初の兆候が現れた時点でプログラムが終了することは望ましくないだろう。
* You probably don't want the programme to teminate at the first sign of trouble.
+
* 続行する場合は、問題領域に割り当てられたメモリが回復されることを確認すること。メモリ リークは発生しないようにする。
* If we do keep going, lets make sure any memory allocated in the problem area is recovered, we don't want any memory leaks.
+
* ただし、さらに詳しく説明する場合は、状況に応じた説明をユーザーに提供しよう。
* If we are going to go under however, lets give the user a context sensitive explanation.
 
  
The following bit of code is based on the above examples but this time it DOES check for errors in critical places. Key is the try...finally...end and try...except...end blocks. You can test it by doing things like uninstalling SQLite3, putting a dummy file in place of the test_dbase database and so on.
+
次のコードは上記の例に基づいているが、今回は重要な場所でのエラーをチェックをする。
 +
鍵はtry...finally...end try...except...end ブロックである。SQLite3をアンインストールして、test_dbaseのあったところにダミーファイルを置くなどして、これをテストすることができる。
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 298: Line 298:
 
         Query.DataBase := Connect;
 
         Query.DataBase := Connect;
 
         Query.SQL.Text:= 'select * from tblNames';
 
         Query.SQL.Text:= 'select * from tblNames';
         Query.Open;          // This will also open Connect
+
         Query.Open;          // これは接続を開始する
 
         while not Query.EOF do begin
 
         while not Query.EOF do begin
 
             writeln('ID: ', Query.FieldByName('ID').AsInteger, '  Name: ' +
 
             writeln('ID: ', Query.FieldByName('ID').AsInteger, '  Name: ' +
Line 317: Line 317:
 
     writeln(Message);
 
     writeln(Message);
 
     writeln(Suggestion);
 
     writeln(Suggestion);
     Connect.Close;              // Its possibly silly worrying about freeing
+
     Connect.Close;              // 解放することを心配するのはばかげているかもしれない。
     Trans.free;                // if we are going to call halt() but its
+
     Trans.free;                // halt()を呼ぶつもりだが
     Connect.Free;              // a demo, alright ?
+
     Connect.Free;              // これはデモだから、いいか ?
 
     halt();
 
     halt();
 
end;
 
end;
Line 330: Line 330:
 
     try
 
     try
 
         if not fileexists(Connect.DatabaseName) then begin
 
         if not fileexists(Connect.DatabaseName) then begin
             Connect.Open;  // give EInOutError if (eg) SQLite not installed
+
             Connect.Open;  // もし例えばSQLiteがインストールされていないときにEInOutErrorをあげる
 
             Trans.StartTransaction;
 
             Trans.StartTransaction;
 
             WriteTable('create table TBLNAMES (ID integer Primary Key, NAME varchar(40));');
 
             WriteTable('create table TBLNAMES (ID integer Primary Key, NAME varchar(40));');
Line 354: Line 354:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== How to execute a query using TSQLQuery? ==
+
== どのようにしてTSQLQuery を用いてクエリを実行するのか ==
Next to statements that return a dataset (see [[#How to read data from a table?]]) SQL has statements that do not return data. For example <tt>INSERT</tt>, <tt>UPDATE</tt> and <tt>DELETE</tt> statements do not return data. These statements can be executed using ''[[#How to execute direct queries/make a table?|TSQLConnection.ExecuteDirect]]'', but TSQLQuery can also be used. If you do not expect return data use ''TSQLQuery.ExecSQL'' instead of ''TSQLQuery.Open''. As mentioned earlier, use ''TSQLQuery.Open'' to open the dataset returned by the SQL statement.
+
次にデータセット([[#どのようにしてテーブルからデータを取得するのか]]参照)を返すステートメントである。SQLはデータを返さないステートメントを持っている。例えば、<tt>INSERT</tt><tt>UPDATE</tt><tt>DELETE</tt>ステートメントはデータを返さない。これらは''[[#どのようにして直接クエリを実行/テーブルを作るのか|TSQLConnection.ExecuteDirect]]''を用いることで実行できるが、TSQLQueryもまた用いることができる。もし、''TSQLQuery.Open'' の代わりに ''TSQLQuery.ExecSQL'' を使って、データが変えることを期待しないなら、先に述べたように、SQLステートメントによりデータセットを開くために、''TSQLQuery.Open'' を用いること。
  
The following procedure creates a table and inserts two records using TSQLQuery.
+
以下は TSQLQuery を用いてテーブルを作り、2つのレコードを作るプロシージャである。
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 383: Line 383:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
== How to use parameters in a query? ==
+
== どのようにしてクエリの中でパラメータを用いるのか ==
In the code example of [[#How to execute a query using TSQLQuery?]] the same query is used twice, only the values to be inserted differ. A better way to do this is by using parameters in the query.
+
In the code example of [[#どのようにしてTSQLQuery を用いてクエリを実行するのか]]のコード例の中で同じクエリが2回使われており、挿入される値が異なるだけである。これを行うより良い方法は、クエリの中でパラメータを用いることである。
  
The syntax of parameters in queries is different per database system, but the differences are handled by TSQLQuery. Replace the values in the query with a colon followed by the name of the parameter you want to use. For example:
+
クエリ内のパラメータの構文はデータベース システムごとに異なるが、その違いは TSQLQuery によって処理される。
 +
 
 +
クエリ内の値をコロンに置き換え、その後に使用するパラメータの名前を続ける。例えば:
 
<syntaxhighlight lang=pascal>Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';</syntaxhighlight>
 
<syntaxhighlight lang=pascal>Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';</syntaxhighlight>
  
This query will create two parameters: 'ID' and 'NAME'.
+
このクエリは2つのパラメータ: 「ID」と「NAME」を作る。
To determine the parameters, the query is parsed at the moment the text of ''TSQLQuery.SQL'' is assigned or changed. All existing parameters will be removed and the new parameters will be added to the 'TSQLQuery.Params' property. Assigning a value to a parameter is similar to assigning a value to a field in the dataset:
+
 
 +
パラメーターを決定するために、''TSQLQuery.SQL'' のテキストが割り当てられるか変更される時点でクエリが解析される。既存のパラメータはすべて削除され、新しいパラメータが「TSQLQuery.Params」プロパティに追加される。パラメーターに値を割り当てることは、データセット内のフィールドに値を割り当てることと似ている:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 396: Line 399:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
You can't tell from the query what kind of data must be stored in the parameter. The data type of the parameter is determined at the moment a value is first assigned to the parameter. By assigning a value using '.AsString', the parameter is assigned the data type 'ftString'. You can determine the data type directly by setting the 'DataType' property. If an incorrect datatype is assigned to the parameter, then problems will occur during opening or executing the query.
+
クエリからは、どのような種類のデータをパラメータに保存する必要があるかを知ることはできない。パラメータのデータ型は、パラメータに値が最初に割り当てられた時点で決定される。「.AsString」を使用して値を割り当てると、パラメータにはデータ型「ftString」が割り当てられる。'DataType' プロパティを設定することで、データ型を直接決定できる。不正なデータ型がパラメータに割り当てられている場合、クエリを開いたり実行したりするときに問題が発生する。
See [[Database field type]] for more information on data types.
+
データ型についてのさらなる情報は [[Database field type]] を参照されたい。
  
=== Select query ===
+
=== Select クエリ ===
  
An example of a select query with parameters would be to change something like this:
+
パラメーターを使用した選択クエリの例は、次のように変更することになる:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 407: Line 410:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
to something like this:
+
をこのように:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 414: Line 417:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Example ===
+
=== 実装例 ===
  
The following example creates the same table as the previous example, but now parameters are used:
+
以下の例は1つ前の例と同じテーブルを生成するが、パラメータが用いられている:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 443: Line 446:
 
     Query.ExecSQL;
 
     Query.ExecSQL;
  
     //Query.UnPrepare; // no need to call this; should be called by Query.Close
+
     //Query.UnPrepare; // これを呼ぶ必要はない; Query.Closeによって呼ばれるはずである
 
     Query.Close;
 
     Query.Close;
 
   finally
 
   finally
Line 451: Line 454:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Notice that this example requires more code than the example without the parameters. Then what is the use of using parameters?  
+
この例はパラメータを用いないコードよりもコード量が多くなることに気を付けること。ではパラメータの用途は何であろうか?  
  
Speed is one of the reasons. The example with parameters is faster, because the database server parses the query only once (in the .Prepare statement or at first run).
+
速度が1つの理由である。このパラメータを用いた例はより実行が速い、何故なら(.Prepareステートメントもしくは最初の実行で)データベースサーバはクエリを1回のみパースするからである。
  
Another reason to use prepared statements is prevention of [http://en.wikipedia.org/wiki/SQL_injection SQL-injection] (see also [[Secure programming]].
+
またプリペアードステートメントを用いる別の理由は、[http://en.wikipedia.org/wiki/SQL_injection SQL-インジェクション]を防ぐことになる ([[Secure programming]]も参照のこと)。
  
Finally, in some cases it just simplifies coding.
+
最後に、ある例ではコーディングを簡素化するためである。
  
== Troubleshooting: TSQLConnection logging ==
+
== 問題解決法: TSQLConnection ログ ==
  
You can let a TSQLConnection log what it is doing. This can be handy to see what your Lazarus program sends to the database exactly, to debug the database components themselves and perhaps to optimize your queries.
+
TSQLConnection の動作をログに記録させることができる。 これは、Lazarus プログラムがデータベースに送信する内容を正確に確認したり、データベース コンポーネント自体をデバッグしたり、クエリを最適化したりする場合に便利である。
NB: if you use prepared statements/parametrized queries (see section above), the parameters are often sent in binary by the TSQLConnection descendent (e.g. TIBConnection), so you can't just copy/paste the logged SQL into a database query tool.
+
注意: 準備されたステートメント/パラメータ化されたクエリ (上記のセクションを参照) を使用する場合、パラメータは多くの場合、TSQLConnection の派生 (TIBConnection など) によってバイナリで送信されるため、ログに記録された SQL をコピーしてデータベース クエリ ツールに貼り付けるだけではすまない。いずれにせよ、接続ログにより、プログラムの動作について多くの洞察が得られる。
Regardless, connection logging can give a lot of insight in what your program is doing.
 
  
Alternatives are:  
+
これに代わるものとして:  
  
# you can use the debugger to step through the database code if you have built FPC (and Lazarus) with debugging enabled.
+
# デバッグを有効にして FPC (および Lazarus) をビルドした場合は、デバッガーを使用してデータベース コードをステップ実行できます。
# if you use ODBC drivers (at least on Windows) you could enable tracelog output in the ODBC control panel.
+
# ODBC ドライバーを使用している場合 (少なくとも Windows では)、ODBC コントロール パネルでトレースログ出力を有効にすることができる。
# many databases allow you to monitor all statements sent to it from a certain IP address/connection.
+
# 多くのデータベースでは、特定の IP アドレス/接続から送信されるすべてのステートメントを監視できる。
  
If you use TSQLConnection logging, two things are required:
+
TSQLConnection ログを使用する場合は、2 つのことが必要である:
  
# indicate which event types your TSQLConnection should log
+
# TSQLConnection がログに記録する必要があるイベント タイプを指定する。
# point TSQLConnection at a function that receives the events and processes them (logs them to file, prints them to screen, etc.).
+
# イベントを受信して処理する関数に TSQLConnection を指定する (イベントをファイルに記録したり、画面に出力したりするなど)
That function must be of type TDBLogNotifyEvent (see sqldb.pp), so it needs this signature:
+
この関数は TDBLogNotifyEvent 型である必要があるため (sqldb.pp を参照)、次の署名が必要である:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 481: Line 483:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== FPC (or: the manual way) ===
+
=== FPC (もしくは: 手動的方法) ===
  
A code snippet can illustrate this:
+
コード スニペットでこれを説明できる:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
uses
 
uses
 
...
 
...
TSQLConnection, //or a child object like TIBConnection, TMSSQLConnection
+
TSQLConnection, //もしくはTIBConnection、TMSSQLConnection といった派生オブジェクト
 
...
 
...
 
var
 
var
 
type  
 
type  
   TMyApplication = class(TCustomApplication); //this is our application that uses the connection
+
   TMyApplication = class(TCustomApplication); //これが接続を用いるアプリケーション
 
...
 
...
 
   private
 
   private
     // This example stores the logged events in this stringlist:
+
     // この例はこの文字列リストにログを格納する:
 
     FConnectionLog: TStringList;
 
     FConnectionLog: TStringList;
 
...
 
...
 
   protected
 
   protected
     // This procedure will receive the events that are logged by the connection:
+
     // このプロシージャは接続によるログのイベントを受け取るだろう:
 
     procedure GetLogEvent(Sender: TSQLConnection; EventType: TDBEventType; Const Msg : String);
 
     procedure GetLogEvent(Sender: TSQLConnection; EventType: TDBEventType; Const Msg : String);
 
...
 
...
 
   procedure TMyApplication.GetLogEvent(Sender: TSQLConnection;
 
   procedure TMyApplication.GetLogEvent(Sender: TSQLConnection;
 
     EventType: TDBEventType; const Msg: String);
 
     EventType: TDBEventType; const Msg: String);
   // The procedure is called by TSQLConnection and saves the received log messages
+
   // TSQLConnectionによってプロシージャが呼ばれ、FConnectionLogに
   // in the FConnectionLog stringlist
+
   // 受け取ったメッセージログが保存される
 
   var
 
   var
 
     Source: string;
 
     Source: string;
 
   begin
 
   begin
     // Nicely right aligned...
+
     // 見た目よく右揃え
 
     case EventType of
 
     case EventType of
 
       detCustom:  Source:='Custom:  ';
 
       detCustom:  Source:='Custom:  ';
Line 523: Line 525:
  
 
...
 
...
   // We do need to tell our TSQLConnection what to log:
+
   // 何をログに残したいか TSQLConnection に知らせる必要がある:
 
     FConnection.LogEvents:=LogAllEvents; //= [detCustom, detPrepare, detExecute, detFetch, detCommit, detRollBack]
 
     FConnection.LogEvents:=LogAllEvents; //= [detCustom, detPrepare, detExecute, detFetch, detCommit, detRollBack]
     // ... and to which procedure the connection should send the events:
+
     // ... そして、どのプロシージャに向けて、接続がイベントを送るべきか:
 
     FConnection.OnLog:=@Self.GetLogEvent;
 
     FConnection.OnLog:=@Self.GetLogEvent;
 
...
 
...
   // now we can use the connection and the FConnectionLog stringlist will fill with log messages.
+
   // これで接続を使用できるようになり、FConnectionLog 文字列リストにログ メッセージが表示される
 
</syntaxhighlight>
 
</syntaxhighlight>
  
You can also use TSQLConnection's GlobalDBLogHook instead to log everything from multiple connections.
+
代わりに TSQLConnection GlobalDBLogHook を使用して、複数の接続からすべてをログに記録することもできる。
  
=== Lazarus (or: the quick way) ===
+
=== Lazarus (もしくは: 速い方法) ===
  
Finally, the description above is the FPC way of doing things as indicated in the introduction; if using Lazarus, a quicker way is to assign an event handler to the TSQLConnection's OnLog event.
+
最後に、上記の説明は、冒頭で示した FPC の方法である。 Lazarus を使用している場合、より簡単な方法は、TSQLConnection の OnLog イベントにイベント ハンドラーを割り当てることである。
  
== See also ==
+
== 以下も参照のこと ==
  
 
* [[Working With TSQLQuery]]
 
* [[Working With TSQLQuery]]

Latest revision as of 17:29, 1 April 2024

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) Nederlands (nl) polski (pl) 中文(中国大陆)‎ (zh_CN)

データベースのポータル

参照:

チュートリアル/練習となる記事:

各種データベース

Advantage - MySQL - MSSQL - Postgres - Interbase - Firebird - Oracle - ODBC - Paradox - SQLite - dBASE - MS Access - Zeos
日本語版メニュー
メインページ - Lazarus Documentation日本語版 - 翻訳ノート - 日本語障害情報


このテキストは「how-to」として作られている。数多くの質問に1つ、1つ答え、どのように様々なクラスを用いるのか説明したい。これらすべての質問は次から次へと沸き起こり、ある種のチュートリアルとなった。

このテキストがFree Pascal同様にLazarusでも用いられるように書くつもりだが、例はFree Pascal(即ち、コンソールアプリケーション)に向けてある。

どこで公式のドキュメンテーションが見つかるか

公式ドキュメンテーションはここを参照されたいSQLDBドキュメンテーション

どのようにしてデータベースサーバに接続するか

SqlDBは直接データベースサーバには接続せず、用いられているデータベースサーバに対応するクライアントを用いる。SqlDBはクライアントライブラリにコマンドを送り、クライアントライブラリがデータベースサーバに接続しコマンドを送る。これはデータベースと接続を持つためにクライアントライブラリがインストールされていなければならないことを意味する。Windowsではクライアントライブラリは通常、.dll、Linux では.so、OS/Xでは.dylibである。

クライアントライブラリが適切にインストールされているときには、TSQLConnectionコンポーネントを用いてデータベースサーバに接続できる。様々なTSQLConnectionコンポーネントが異なるデータベースサーバに対して利用可能である(SQLdb_Packageを参照):

MySQLに対する注意 - クライアントとコネクションが相互交換できないほどクライアントバージョン間に多くの相違がある。もしクライアントライブラリ バージョン 4.1がインストールされているとすると、TMySQL41Connectionを用いなければならない。これはMySQLサーバには関連しておらずMySQL 4.1クライアントライブラリを用いて、おそらくMySQL 5.0サーバに接続できる(どの組み合わせがサポートされているかはMySQLドキュメンテーションを参照されたい)。

さまざまなデータベースで細部は異なるが、一般的にデータベースサーバに接続するために4つのプロパティを設定しなければならない:

  • サーバの名前とIPアドレス
  • データベースの名前
  • ユーザー名
  • パスワード

これらのプロパティが設定されると、サーバに「open」メソッドを作ることができる。もし接続に失敗するとEDatabaseError例外が生じる。データベースサーバに接続が確立しているかどうか確かめるために、「connected」プロパティを用いること。

Program ConnectDB;

{$mode objfpc}{$H+}

uses
  IBConnection;

function CreateConnection: TIBConnection;
begin
  result := TIBConnection.Create(nil);
  result.Hostname := 'localhost';
  result.DatabaseName := '/opt/firebird/examples/employee.fdb';
  result.UserName := 'sysdba';
  result.Password := 'masterkey';
end;
 
var   
  AConnection : TIBConnection;

begin
  AConnection := CreateConnection;
  AConnection.Open;
  if Aconnection.Connected then
    writeln('Successful connect!')
  else
    writeln('This is not possible, because if the connection failed, ' +
            'an exception should be raised, so this code would not ' +
            'be executed');
  AConnection.Close;
  AConnection.Free;
end.

もし、エラーが発生したら、注意してエラーメッセージを読むこと。データベースサーバが稼働していない、ユーザー名もしくはパスワードが誤っている、データベース名またはIPアドレスが誤って入力されたかもしれない。もしエラーメッセージがクライアントライブラリが見つからないと言っているなら、クライアントライブラリが正しくインストールされているか確かめること。しばしばエラーメッセージは探しているファイル名を文字通り言う。

どのようにして直接クエリを実行/テーブルを作るのか

SqlDB - 文字通り名前がすべてを体現している - はSQLを用いて唯一、データベースサーバとやり取りをする。SQL は 「Structured Query Language」を意味する。 SQLはリレーショナルデータベースとやり取りすることを可能にするために開発された言語である。実際にはすべてのデータベースシステムはそれぞれ方言を持っているが、SQLステートメントの多くはすべてのデータベースシステムで共通している。

FPCでは、以下の違いがある:

  • 情報(データセット)を返すSQLステートメント。このため、TSQLQueryコンポーネントを用いなければならない; #How to read data from a table?を参照のこと。
  • 情報を返さずに、変わって何かを行うステートメント、例えば、データを更新する。このため、またTSQLConnectionの「ExecuteDirect」メソッドを使うかもしれない(データセットを引き戻すためにこれを使うこともできるが、結果に興味はない、例えばセレクタブルストアドプロシージャ)。

大半のデータベースシステムはトランザクションでSQLステートメントを実行する。もし、他のトランザクションの中で利用できるトランザクションの中で行われる変更を望む、もしくはトランザクションが閉じた後でそれらの変更する(!)ならば、トランザクションを「commit」しなければならない。

トランザクションをサポートするために、SqlDBはTSQLTransactionコンポーネントを含んでいる。SqlDBによって実行されるSQLステートメントはデータベースシステムが、トランザクションをサポートしていなくとも、常にトランザクションの最中に行われる必要がある。同様に、TSQLTransactionがいまだにサポートしていないトランザクションをサポートしているデータベースシステムも存在する。その時ですらTSQLTransactionコンポーネントを使わなければならない。

SQLステートメントを実行するためにTSQLConnection.ExecuteDirectを使うには、どの「Transaction」が使われなければならないかを明示しなければならない。逆に、TSQLTransactionを使うためには、どのTSQLTransactionコンポーネントが使われるかを明示しなければならない。

以下の例は「NAME」と「ID」フィールドを持つ「TBLNAMES」を生成し2つのレコードを挿入する。今回はSQLiteを用いる。使われるSQLステートメントは説明されない。SQLステートメント、その使用と文法に関しては、データベースシステムのドキュメンテーションを参照されたい。 この例ではいかなるエラーの捕捉も試みないことに注意すること、それは悪いことである! 例外を調べていただきたい。

program CreateTable;
{$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
uses
  sqldb, sqlite3conn; 
 
var 
  AConnection : TSQLite3Connection;
  ATransaction : TSQLTransaction;
 
begin
  AConnection := TSQLite3Connection.Create(nil);
  AConnection.DatabaseName := 'test_dbase';
 
  ATransaction := TSQLTransaction.Create(AConnection);
  AConnection.Transaction := ATransaction;
  AConnection.Open;
  ATransaction.StartTransaction;
  AConnection.ExecuteDirect('create table TBLNAMES (ID integer, NAME varchar(40));'); 
 
  ATransaction.Commit;
 
  ATransaction.StartTransaction;
  AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (1,''Name1'');'); 
  AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (2,''Name2'');'); 
  ATransaction.Commit; 
  AConnection.Close;
  ATransaction.Free;	
  AConnection.Free;
end.

どのようにしてテーブルからデータを取得するのか

テーブルからデータを読み込むときにはTSQLQueryを用いること。TSQLQueryコンポーネントはその仕事をするために、TSQLConnectionコンポーネントとTSQLTransactionコンポーネントに接続されなければない。TSQLConnectionとTSQLTransactionの設定は#How to connect to a database server? #How to execute direct queries/make a table?で議論する。

TSQLConnectionの時、TSQLTransactionとTSQLQueryが接続される、そしてTSQLQueryは動作するためにさらに設定される。TSQLQueryはTStringsオブジェクトを含む「SQL」プロパティを持つ。「SQL」プロパティは実行されるべきSQLステートメントを含んでいる。もしテーブルtablenameのすべてのデータを読まなければならないときには、「SQL」プロパティを以下のように設定する:

'SELECT * FROM tablename;'

サーバからテーブルを読み込み、TSQLQueryデータセットにデータを読み込むには「open」を使うこと。データは「close」を用いてクエリが閉じられるまでTSQLQueryを通してアクセスできる。

TSQLQueryはTDatasetのサブクラスである。TDatasetはテーブルのすべてのカラムを含む「Field」コレクションを持っている。TDatasetはまた、現在のレコードを追跡する。現在のレコードを変更するために「First」、「Next」、「Prior」、「Last」を用いること。もし最初のレコードが届いたら「Bof」が「True」となり、もし最後のレコードに達したら、「Eof」が「True」になる。現在のレコードの値を読むためには初めに正しい「TField」オブジェクトを見つけ、そして「AsString」、「AsInteger」を用いること。

例: テーブルからデータを取得する

以下は上のCreateTable例で作られたテーブルのすべての値を表示する例である。test_databaseファイルをShowDataワーキングディレクトリにコピーすること。エラーチェックを行っていないことに再び注意すること!

Program ShowData;
 {$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
uses
  DB, Sysutils, sqldb, sqlite3conn;

var
  AConnection  : TSQLConnection;
  ATransaction : TSQLTransaction;
  Query        : TSQLQuery;
 
begin
  AConnection := TSQLite3Connection.Create(nil);
  ATransaction := TSQLTransaction.Create(AConnection);
  AConnection.Transaction := ATransaction;
  AConnection.DatabaseName := 'test_dbase';
  Query := TSQLQuery.Create(nil);
  Query.SQL.Text := 'select * from tblNames';
  Query.Database := AConnection;
  Query.Open;
  while not Query.Eof do
  begin
    Writeln('ID: ', Query.FieldByName('ID').AsInteger, 'Name: ' +
                                  Query.FieldByName('Name').AsString);
    Query.Next;
  end;
  Query.Close;
  AConnection.Close;
  Query.Free;
  ATransaction.Free;
  AConnection.Free;
end.

もちろん、上記のコードは未完成で、「try...finally」ブロックを欠く。しかし、上のコードはデータベースコードを示す意図のもので、そのため仕上げの一筆は省いてある。 「TSQLTransaction.StartTransaction」が使われてないことに注意してほしい。これは不要である。TSQLQueryが開かれるとき、SQLステートメントが実行され、もしトランザクションが利用できないときにはトランザクションが自動的に始まる。プログラマは明示的にトランザクションを開始させる必要はない。 同様のことがTSQLConnectionによって維持されている接続にも適用される。接続は必要に応じて開かれ、「Aconnection.Open」は全く必要ない。もし、TSQLTransactionが破棄されると、自動的「rollback」が実行される。トランザクションに含まれるデータに対する、ありえる変更は失われるだろう。

なぜ TSQLQuery.RecordCount はいつも10を返すのか

データセットのレコードを数えるためには、「.RecordCount」を用いること。しかし、「.RecordCount」はサーバからすでに送り出されたレコードの数を示している。性能上の理由により、SqlDBはデフォルトではTSQLQueryを開いたときすべてのレコードを読まず、最初の10を読む。11番目のレコードを読むときのみに次の10レコードがアクセスされる、など。「.Last」を用いると、すべてのレコードが読み込まれる。 サーバにあるレコードの真の数を知りたいときは、初めに「.Last」、そして「.RecordCount」を呼ぶことができる。 また別のやり方もある。サーバによって返されるレコードの数は「.PacketRecords」プロパティにセットされる。そのデフォルト値は10、これを-1に設定すると、一度にすべてのレコードが読み込まれる。

現在の安定版のFPCでは、.RecordCountはフィルタを考慮に入れていない。即ち、それはフィルタをかけられない総和となる。

もしレコードの正確な数を必要としているのであれば、ほかのSQLクエリをクエリの中で用いてレコードの数を直接問い合わせるのがしばしばより良い考えであるが、その間に他のトランザクションがレコードの数を変えたかもしれないので、同じトランザクションの中でそれをしなければならないだろう。


Lazarus

Lazarusはフォーム上にTDatasetからのデータを示すために様々なコンポーネントを持っている。正しいTSQLConnection、TSQLTransaction、TSQLQueryコンポーネントをフォームに置き、そしてそれらを適切に接続、設定すること。加えて、TDatasourceが必要となり、「TDatasource.Dataset」プロパティを、用いるTSQLQueryコンポーネントに設定すること。 (注意「TSQLQuery.Datasource」プロパティを、用いるTDatasourceコンポーネントに設定しないこと。「TSQLQuery.Datasource」プロパティはmaster-detailテーブルのみに使われる - MasterDetail参照)。結果としてTDGridをフォームに配置して、前に加えたTDatasourceをグリッドのプロパティに設定するることになる。

これらすべてが機能していることを見るために、TSQLConnectionの「Connected」プロパティをLazarus IDEで「True」に設定すること。IDEはデータベースサーバへ直ちに接続を試みるだろう。もし、これが動いたら、「TSQLQuery.Active」プロパティを「True」に設定できる。もしすべてが正しいなら、IDEの中に、直ちにテーブルからのすべてのデータがみられるだろう。

どのようにしてテーブルのデータを変更するのか

レコード内のデータを変更するための一般的なプロセスは、TSQLQuery を取得して変更するレコードを検索し、そこで変更を加えてデータベースにプッシュし直すことである。TDataSet (TSQLQuery の派生元) は編集モードに設定する必要がある。 編集モードに入るには、「.Edit」、「.Insert」、または「.Append」メソッドを呼び出す。 現在のレコードを変更するには、「.Edit」メソッドを使用する。現在のレコードを変更するには、「.Edit」メソッドを使用する。 現在のレコードの前に新しいレコードを挿入するには、「.Insert」を使用する。 テーブルの最後に新しいレコードを挿入するには、「.Append」を使用する。編集モードでは、「フィールド」プロパティを通じてフィールド値を変更できる。「Post」を使用して新しいデータを検証する。データが有効な場合は、編集モードが終了する。たとえば「.Next」を使用して別のレコードに移動し、データセットが編集モードの場合、最初に「.Post」が呼び出される。「.Cancel」 を使用すると、最後の 「.Post」 呼び出し以降に加えたすべての変更が破棄され、編集モードが終了します。

Query.Edit;
Query.FieldByName('NAME').AsString := 'Edited name';
Query.Post;

上記はまだ完全な話ではない。 TSQLQuery は、バッファされた更新を利用する TBufDataset から派生している。バッファ更新とは、「Post」を呼び出した後、データセット内の変更がすぐに表示されるが、データベース サーバーには送信されないことを意味する。実際に起こるのは、変更が変更ログに保持されることである。「.ApplyUpdates」メソッドが呼び出されると、変更ログ内のすべての変更がデータベースに送信される。そうして初めて、データベース サーバーは変更を認識する。変更は、TSQLTransaction のトランザクション内でサーバーに送信される。「ApplyUpdates」の前にトランザクションを正しく設定すること。更新を適用した後、コミットを実行して変更をデータベース サーバーに保存する必要がある。

以下は、テーブル内のデータを変更し、変更をサーバーに送信し、トランザクションをコミットする例である。 繰り返すが、エラーチェックがない。これは非常に芳しくない!

Program EditData;
{$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
uses
    db, sqldb, sqlite3conn;
var 
  AConnection : TSQLConnection;
  ATransaction : TSQLTransaction;
  Query : TSQLQuery;
 
begin
  AConnection := TSQLite3Connection.Create(nil);
  ATransaction := TSQLTransaction.Create(AConnection);
  AConnection.Transaction := ATransaction;
  AConnection.DatabaseName := 'test_dbase';
  Query := TSQLQuery.Create(nil);
  Query.DataBase := AConnection;
  Query.SQL.Text := 'select * from tblNames where ID = 2';
  Query.Open;
  Query.Edit;
  Query.FieldByName('NAME').AsString := 'Name Number 2';
  Query.Post;
  Query.UpdateMode := upWhereAll;         // dbで定義されている
  Query.ApplyUpdates;
  ATransaction.Commit;
  Query.Free;
  ATransaction.Free;
  AConnection.Free;
end.

実際の作業は、変更するレコードを識別する SQL ステートメント「select * from tblNames where ID = 2」から始まる。 「where ID = 2」ビットを省略すると、TSQLQuery は明らかに ID (および他の整数フィールド?) を 1 に設定する。そのため、ID=1 の行のみで動作する。「UpdateMode」については、引き続き読み進められたい。

どのようにして SqlDB はデータベースサーバに変更を送るのか

#どのようにしてテーブルのデータを変更するのかのコード例で以下が、何をしているのか説明なしに記載されている。

Query.UpdateMode := upWhereAll;

その行が何をしているのかを知る最良の方法は、その行を省略することだ。 このステートメントを省略し、この Howto に正確に従った場合、次のエラー メッセージが表示される:

No update query specified and failed to generate one. (No fields for inclusion in where statement found)

何が問題なのかを理解するには、変更がどのようにデータベース サーバーに送信されるかを理解する必要がある。SQL サーバー内のデータを取得する唯一の方法は、SQL クエリを実行することだ。SQL には、レコードを操作する 3 つの異なる方法に対応する 3 種類のクエリがある。新しいレコードの作成、レコードの変更または削除には、それぞれ、insert、update、delete ステートメントが実行される。アップデートステートメントは以下のようなものだろう:

update TBLNAMES set NAME='Edited name' where ID=1;

変更をデータベース サーバーに送信するには、Sqldb は更新クエリを組み立てる必要がある。クエリを組み立てるには、以下の3つが必要である:

テーブルの名前
テーブル名は選択クエリの解析から取得されるが、これは常に機能するとは限らない。
UPDATE もしくは INSERT
これらには、変更する必要があるフィールドが含まれている。
WHERE
これには、どのレコードを変更する必要があるかを決定するフィールドが含まれる。

すべてのフィールド(Fieldの各TField) には ProviderFlags プロパティがある。ProviderFlagspfInUpdate があるフィールドのみが、クエリの更新または挿入条件で使用される。デフォルトではすべてのフィールドの ProviderFlags プロパティに pfInUpdate が設定されている。WHERE 句でどのフィールドが使用されるかは、クエリの UpdateMode プロパティとフィールドの ProviderFlags プロパティによって異なる。「ProviderFlags」に「pfInkey」が含まれるフィールドは、常に WHERE 句で使用されます。フィールドがテーブルの主キーの一部であり、「TSQLQuery.UsePrimaryKeyAsKey」が「True」を返す場合、フィールドには自動的に「pfInKey」フラグが設定されます。クエリの UpdateMode のデフォルト値は upWhereKeyOnly である。この更新モードでは、ProviderFlags プロパティに pfInkey を持つフィールドのみが WHERE 句で使用される。どのフィールドにも pfInKey フラグが設定されていない場合、WHERE 句に使用できるフィールドはなく、このセクションの冒頭のエラー メッセージが返される。

この問題は以下で解決される:

  • テーブルにプライマリキーを加え、TSQLQuery.UsePrimaryKeyAsKey を 「True」に設定するか、
  • コードの中でpfInkeyフラグを1つ、またはそれ以上のフィールドに設定する。

UpdateMode プロパティは、さらに 2 つの可能な値を認識します。 「upWhereAll」を使用すると、「pfInWhere」フラグが WHERE 句に設定されているすべてのフィールドを追加できる。UpdateModeプロパティは2つの可能な値を知っている。デフォルトでは、すべてのフィールドにこのフラグが設定されている。「upWhereChanged」は 「pfInWhere」フラグセットを持つフィールド および 現在のレコードで、変更されたフィールドに用いることができる。

エラーの取り扱い方

実行時エラーは避けられず、ディスクがいっぱいになる、必要なライブラリやヘルパー アプリケーションが利用できない、等の問題が発生する可能性があるため、それを許容する必要がある。 FPC は実行時エラーを非常に適切に検出して処理する。通常、何が問題だったのかについて簡潔かつ合理的な説明が得られるが、多くの理由でエラーを監視し、取り扱うことを望むだろう -

  • おそらく、問題の最初の兆候が現れた時点でプログラムが終了することは望ましくないだろう。
  • 続行する場合は、問題領域に割り当てられたメモリが回復されることを確認すること。メモリ リークは発生しないようにする。
  • ただし、さらに詳しく説明する場合は、状況に応じた説明をユーザーに提供しよう。

次のコードは上記の例に基づいているが、今回は重要な場所でのエラーをチェックをする。 鍵はtry...finally...end と try...except...end ブロックである。SQLite3をアンインストールして、test_dbaseのあったところにダミーファイルを置くなどして、これをテストすることができる。

program DemoDBaseWithErrors;
{$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
uses
  DB, Sysutils, sqldb, sqlite3conn;
var
    Connect : TSQLite3Connection;
    Trans : TSQLTransaction;


procedure WriteTable (Command : string);
begin
	Connect.ExecuteDirect(Command);
    	Trans.Commit;
end;

procedure ReadTable ();
var
   Query : TSQLQuery;
   Count : smallint;
begin
    Count := 0;
    try
        Query := TSQLQuery.Create(nil);
        Query.DataBase := Connect;
        Query.SQL.Text:= 'select * from tblNames';
        Query.Open;          // これは接続を開始する
        while not Query.EOF do begin
            writeln('ID: ', Query.FieldByName('ID').AsInteger, '  Name: ' +
                              Query.FieldByName('Name').AsString);
            Query.Next;
            Count := Count + 1;
        end;
    finally
        Query.Close;
        Query.Free;
    end;
    writeln('Found a total of ' + InttoStr(Count) + ' lines.');
end;

procedure FatalError(ClassName, Message, Suggestion : string);
begin
    writeln(ClassName);
    writeln(Message);
    writeln(Suggestion);
    Connect.Close;              // 解放することを心配するのはばかげているかもしれない。
    Trans.free;                 // halt()を呼ぶつもりだが
    Connect.Free;               // これはデモだから、いいか ?
    halt();
end;

begin
    Connect := TSQLite3Connection.Create(nil);
    Trans := TSQLTransaction.Create(Connect);
	Connect.Transaction := Trans;
    Connect.DatabaseName := 'test_dbase';
    try
        if not fileexists(Connect.DatabaseName) then begin
            Connect.Open;   // もし例えばSQLiteがインストールされていないときにEInOutErrorをあげる
            Trans.StartTransaction;
            WriteTable('create table TBLNAMES (ID integer Primary Key, NAME varchar(40));');
            Trans.Commit;
        end;
        Connect.open;
        Trans.StartTransaction;
        WriteTable('insert into TBLNAMES (NAME) values (''AName1'');');
        WriteTable('insert into TBLNAMES (NAME) values (''AName2'');');
    except
        on E : EDatabaseError do
            FatalError(E.ClassName, E.Message, 'Does the file contain the correct database ?');
        on E : EInOutError do
            FatalError(E.ClassName, E.Message, 'Have you installed SQLite (and dev package)?');
        on E : Exception do
            FatalError(E.ClassName, E.Message, 'Something really really bad happened.');
     end;
    ReadTable();
    Connect.Close;
    Trans.Free;
    Connect.Free;
end.

どのようにしてTSQLQuery を用いてクエリを実行するのか

次にデータセット(#どのようにしてテーブルからデータを取得するのか参照)を返すステートメントである。SQLはデータを返さないステートメントを持っている。例えば、INSERTUPDATEDELETEステートメントはデータを返さない。これらはTSQLConnection.ExecuteDirectを用いることで実行できるが、TSQLQueryもまた用いることができる。もし、TSQLQuery.Open の代わりに TSQLQuery.ExecSQL を使って、データが変えることを期待しないなら、先に述べたように、SQLステートメントによりデータセットを開くために、TSQLQuery.Open を用いること。

以下は TSQLQuery を用いてテーブルを作り、2つのレコードを作るプロシージャである。

procedure CreateTable;
  
var 
  Query : TSQLQuery;
  
begin
  Query := TSQLQuery.Create(nil);
  try
    Query.Database := AConnection;

    Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
    Query.ExecSQL;
 
    Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (1,''Name1'');';
    Query.ExecSQL;
  
    Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (2,''Name2'');';
    Query.ExecSQL;
  finally
    Query.Free;
  end;
end;

どのようにしてクエリの中でパラメータを用いるのか

In the code example of #どのようにしてTSQLQuery を用いてクエリを実行するのかのコード例の中で同じクエリが2回使われており、挿入される値が異なるだけである。これを行うより良い方法は、クエリの中でパラメータを用いることである。

クエリ内のパラメータの構文はデータベース システムごとに異なるが、その違いは TSQLQuery によって処理される。

クエリ内の値をコロンに置き換え、その後に使用するパラメータの名前を続ける。例えば:

Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';

このクエリは2つのパラメータ: 「ID」と「NAME」を作る。

パラメーターを決定するために、TSQLQuery.SQL のテキストが割り当てられるか変更される時点でクエリが解析される。既存のパラメータはすべて削除され、新しいパラメータが「TSQLQuery.Params」プロパティに追加される。パラメーターに値を割り当てることは、データセット内のフィールドに値を割り当てることと似ている:

Query.Params.ParamByName('Name').AsString := 'Name1';

クエリからは、どのような種類のデータをパラメータに保存する必要があるかを知ることはできない。パラメータのデータ型は、パラメータに値が最初に割り当てられた時点で決定される。「.AsString」を使用して値を割り当てると、パラメータにはデータ型「ftString」が割り当てられる。'DataType' プロパティを設定することで、データ型を直接決定できる。不正なデータ型がパラメータに割り当てられている場合、クエリを開いたり実行したりするときに問題が発生する。 データ型についてのさらなる情報は Database field type を参照されたい。

Select クエリ

パラメーターを使用した選択クエリの例は、次のように変更することになる:

  Query.SQL.Text := 'select ID,NAME from TBLNAMES where NAME = '''+Edit1.Text+''' ORDER BY NAME ';

をこのように:

  Query.SQL.Text := 'select ID,NAME from TBLNAMES where NAME = :NAMEPARAM ORDER BY NAME ';
  Query.Params.ParamByName('NAMEPARAM').AsString := Edit1.Text;

実装例

以下の例は1つ前の例と同じテーブルを生成するが、パラメータが用いられている:

procedure CreateTableUsingParameters;
  
var 
  Query : TSQLQuery;
  
begin
  Query := TSQLQuery.Create(nil);
  try
    Query.Database := AConnection;

    Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
    Query.ExecSQL;

    Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';
    Query.Prepare;
  
    Query.Params.ParamByName('ID').AsInteger := 1;
    Query.Params.ParamByName('NAME').AsString := 'Name1';
    Query.ExecSQL;
  
    Query.Params.ParamByName('ID').AsInteger := 2;
    Query.Params.ParamByName('NAME').AsString := 'Name2';
    Query.ExecSQL;

    //Query.UnPrepare; // これを呼ぶ必要はない; Query.Closeによって呼ばれるはずである
    Query.Close;
  finally
    Query.Free;
  end;
end;

この例はパラメータを用いないコードよりもコード量が多くなることに気を付けること。ではパラメータの用途は何であろうか?

速度が1つの理由である。このパラメータを用いた例はより実行が速い、何故なら(.Prepareステートメントもしくは最初の実行で)データベースサーバはクエリを1回のみパースするからである。

またプリペアードステートメントを用いる別の理由は、SQL-インジェクションを防ぐことになる (Secure programmingも参照のこと)。

最後に、ある例ではコーディングを簡素化するためである。

問題解決法: TSQLConnection ログ

TSQLConnection の動作をログに記録させることができる。 これは、Lazarus プログラムがデータベースに送信する内容を正確に確認したり、データベース コンポーネント自体をデバッグしたり、クエリを最適化したりする場合に便利である。 注意: 準備されたステートメント/パラメータ化されたクエリ (上記のセクションを参照) を使用する場合、パラメータは多くの場合、TSQLConnection の派生 (TIBConnection など) によってバイナリで送信されるため、ログに記録された SQL をコピーしてデータベース クエリ ツールに貼り付けるだけではすまない。いずれにせよ、接続ログにより、プログラムの動作について多くの洞察が得られる。

これに代わるものとして:

  1. デバッグを有効にして FPC (および Lazarus) をビルドした場合は、デバッガーを使用してデータベース コードをステップ実行できます。
  2. ODBC ドライバーを使用している場合 (少なくとも Windows では)、ODBC コントロール パネルでトレースログ出力を有効にすることができる。
  3. 多くのデータベースでは、特定の IP アドレス/接続から送信されるすべてのステートメントを監視できる。

TSQLConnection ログを使用する場合は、2 つのことが必要である:

  1. TSQLConnection がログに記録する必要があるイベント タイプを指定する。
  2. イベントを受信して処理する関数に TSQLConnection を指定する (イベントをファイルに記録したり、画面に出力したりするなど)。

この関数は TDBLogNotifyEvent 型である必要があるため (sqldb.pp を参照)、次の署名が必要である:

TDBLogNotifyEvent = Procedure (Sender : TSQLConnection; EventType : TDBEventType; Const Msg : String) of object;

FPC (もしくは: 手動的方法)

コード スニペットでこれを説明できる:

uses
...
TSQLConnection, //もしくはTIBConnection、TMSSQLConnection といった派生オブジェクト
...
var
type 
  TMyApplication = class(TCustomApplication); //これが接続を用いるアプリケーション
...
  private
    // この例はこの文字列リストにログを格納する:
    FConnectionLog: TStringList;
...
  protected
    // このプロシージャは接続によるログのイベントを受け取るだろう:
    procedure GetLogEvent(Sender: TSQLConnection; EventType: TDBEventType; Const Msg : String);
...
  procedure TMyApplication.GetLogEvent(Sender: TSQLConnection;
    EventType: TDBEventType; const Msg: String);
  // TSQLConnectionによってプロシージャが呼ばれ、FConnectionLogに
  // 受け取ったメッセージログが保存される
  var
    Source: string;
  begin
    // 見た目よく右揃え
    case EventType of
      detCustom:   Source:='Custom:  ';
      detPrepare:  Source:='Prepare: ';
      detExecute:  Source:='Execute: ';
      detFetch:    Source:='Fetch:   ';
      detCommit:   Source:='Commit:  ';
      detRollBack: Source:='Rollback:';
      else Source:='Unknown event. Please fix program code.';
    end;
    FConnectionLog.Add(Source + ' ' + Msg);
  end;

...
  // 何をログに残したいか TSQLConnection に知らせる必要がある:
    FConnection.LogEvents:=LogAllEvents; //= [detCustom, detPrepare, detExecute, detFetch, detCommit, detRollBack]
    // ... そして、どのプロシージャに向けて、接続がイベントを送るべきか:
    FConnection.OnLog:=@Self.GetLogEvent;
...
  // これで接続を使用できるようになり、FConnectionLog 文字列リストにログ メッセージが表示される

代わりに TSQLConnection の GlobalDBLogHook を使用して、複数の接続からすべてをログに記録することもできる。

Lazarus (もしくは: 速い方法)

最後に、上記の説明は、冒頭で示した FPC の方法である。 Lazarus を使用している場合、より簡単な方法は、TSQLConnection の OnLog イベントにイベント ハンドラーを割り当てることである。

以下も参照のこと