Difference between revisions of "SQLdb Tutorial3/ja"

From Free Pascal wiki
Jump to navigationJump to search
 
(17 intermediate revisions by the same user not shown)
Line 3: Line 3:
 
{{Infobox databases/ja}}
 
{{Infobox databases/ja}}
 
== 概要 ==
 
== 概要 ==
In this tutorial, you will learn how to
+
このチュートリアルでは、次の方法を学ぶ
* make your application suitable for multiple databases, including using a login form
+
* ログインフォームの使用など、アプリケーションを複数のデータベースに適したものにする
* get database data into normal controls (instead of database controls)
+
* データベース データを (データベース コントロールではなく) 通常のコントロールに取得する
* get data out of the controls and back into the database
+
* コントロールからデータを取得してデータベースに戻す
* execute parametrized queries.
+
* パラメータ化されたクエリを実行する。
  
== Multi database support ==
+
== マルチデータベースのサポート ==
Using any database and a login form, you can support multiple different database servers/embedded libraries that SQLDB supports.
+
任意のデータベースとログイン フォームを使用して、SQLDB がサポートする複数の異なるデータベース サーバー/組み込みライブラリをサポートできる。
  
Advantages:
+
利点:
* the user/programmer can dynamically use any sqldb t*connection, so he can choose between dbs
+
* ユーザー/プログラマは、任意の sqldb t*connection を動的に使用できるため、データベースを選択できる。
  
Disadvantages:
+
短所:
* More complicated SQL will likely need to still be adapted. Each database has its own dialect; it is of course possible to call db-specific SQL, this can grow into a maintenance problem.
+
* より複雑な SQL をさらに調整する必要がある可能性がある。 各データベースには独自の方言がある。 もちろん、データベース固有の SQL を呼び出すことも可能だが、これはメンテナンスの問題に発展する可能性がある。
* You can't use T*connection specific properties (like ''TIBConnection.Dialect'' to set Firebird dialect).
+
* T*connection 固有のプロパティ (Firebird 方言を設定するための ''TIBConnection.Dialect'' など) は使用できない。
 +
 
 +
マルチデータベース サポートを使用するには、''TIBConnection'' などの特定の T*Connection の代わりに、''TSQLConnector'' (''TSQLConnection'' ではない) を使用する。これにより、(プログラムの実行中に) 特定の T*Connection が動的に選択される。
 +
''ConnectorType''プロパティに基づいてT*Connectionを使用する:
  
To use multi-database support, instead of your specific T*Connection such as ''TIBConnection'', use ''TSQLConnector'' (not ''TSQLConnection''), which dynamically (when the program is running) chooses the specific T*Connection to use based on its ''ConnectorType'' property:
 
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
uses
 
uses
Line 28: Line 30:
 
   Conn:=TSQLConnector.Create(nil);
 
   Conn:=TSQLConnector.Create(nil);
 
   try
 
   try
    // ...actual connector type is determined by this property.
+
    // ...実際のコネクタのタイプはこのプロパティによって決まる。
    // Make sure the ChosenConfig.DBType string matches
+
    // ChosenConfig.DBType 文字列が一致することを確認する
    // the connectortype (e.g. see the string in the
+
    // コネクタのタイプ (例:
    // T*ConnectionDef.TypeName for that connector .
+
    // そのコネクタの T*ConnectionDef.TypeName
 
     Conn.ConnectorType:=ChosenConfig.DBType;
 
     Conn.ConnectorType:=ChosenConfig.DBType;
     // the rest is as usual:
+
     // 残りは通常通り:
 
     Conn.HostName:='DBSERVER';
 
     Conn.HostName:='DBSERVER';
 
     Conn.DatabaseName:='bigdatabase.fdb';
 
     Conn.DatabaseName:='bigdatabase.fdb';
Line 42: Line 44:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Login form ==
+
== ログインフォーム ==
As mentioned in [[SQLdb Tutorial1]], a user should login to the database using a form (or perhaps a configuration file that is securely stored), not via hardcoded credentials in the application. Besides security considerations, having to recompile the application whenever the database server information changes is not a very good idea.
+
[[SQLdb Tutorial1/ja]] で説明したように、ユーザーはアプリケーションにハードコードされた資格情報を使用するのではなく、フォーム (または安全に保存されている構成ファイル) を使用してデータベースにログインする必要がある。 セキュリティ上の考慮事項に加えて、データベース サーバーの情報が変更されるたびにアプリケーションを再コンパイルしなければならないのは、あまり良い考えではない。
  
In ''dbconfiggui.pas'', we will set up a login form that pulls in default values from an ini file, if it exists. This allows you to set up a default connection with some details (database server, database name) filled out for e.g. enterprise deployments.
+
''dbconfiggui.pas'' では、ini ファイルが存在する場合はそこからデフォルト値を取得するログイン フォームをセットアップする。 これにより、エンタープライズ展開、いくつかの詳細 (データベース サーバ、データベース名) を入力してデフォルトの接続を設定できる。
In the form, the user can add/edit his username/password, and test the connection before going further.
+
このフォームでは、ユーザーは自分のユーザー名/パスワードを追加/編集し、次に進む前に接続をテストできる。
  
 
[[File:dbloginform.png]]
 
[[File:dbloginform.png]]
  
We use a separate dbconfig.pas unit with a ''TDBConnectionConfig'' class to store our chosen connection details. This class has support for reading default settings from an ini file.
+
''TDBConnectionConfig'' クラスを持つ別の dbconfig.pas ユニットを使用して、選択した接続の詳細を保存します。 このクラスは、ini ファイルからのデフォルト設定の読み取りをサポートしています。
  
This allows use without GUI login forms (e.g. when running unattended/batch operations), and allows reuse in e.g. web applications.
+
これにより、GUI ログイン フォームなしでの使用 (例: 無人/バッチ操作の実行時) が可能になり、たとえば、Web サイトでの再利用が可能になる。
  
This ''TDBConnectionConfig'' class is surfaced in the login form as the ''Config'' property, so that the main program can show the config form modally, detect an OK click by the user and retrieve the selected configuration before closing the config form.
+
この''TDBConnectionConfig''クラスはログイン フォームに''Config''プロパティとして表示されるため、メイン プログラムは設定フォームをモーダルに表示し、ユーザーによる [OK] クリックを検出して、フォーム設定を閉じる前に選択した設定を取得できる。
  
=== Connection test callback function ===
+
=== 接続テストコールバック関数 ===
To keep the login form flexible (it may be used with other database layers like Zeos), we implement the test section as a callback function and let the main program deal with it.
+
ログイン フォームを柔軟に保つために (Zeos などの他のデータベース層で使用できる)、テスト セクションをコールバック関数として実装し、メイン プログラムで処理させる。
  
The definition in the login form in ''dbconfiggui.pas'':
+
''dbconfiggui.pas'' のログイン フォームの定義:
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
type
 
type
  TConnectionTestFunction = function(ChosenConfig: TDBConnectionConfig): boolean of object;
+
  TConnectionTestFunction = function(ChosenConfig: TDBConnectionConfig): オブジェクトのブール値。
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The main form must implement a function that matches this definition to handle the test request from the config form.
+
メイン フォームは、構成フォームからのテスト リクエストを処理するために、この定義に一致する関数を実装する必要がある。
  
The callback function takes the config object passed on by the config form, and uses that to construct a connection with the chosen database type. It then simply tries to connect with the server; if it is succesful, it sets the function result to true, otherwise the result stays false.
+
コールバック関数は、構成フォームによって渡された構成オブジェクトを受け取り、それを使用して、選択されたデータベース タイプとの接続を構築する。 その後、単純にサーバーへの接続を試行する。 成功した場合は関数の結果を true に設定し、そうでない場合は結果は false のままとなる。
 
 
Because database connection attempts to non-existing servers can have a long timeout, we indicate to the user that he must wait by setting the cursor to the hourglass icon.
 
  
 +
存在しないサーバーへのデータベース接続の試行は長いタイムアウトになる可能性があるため、カーソルを砂時計アイコンに設定して待機する必要があることをユーザーに示す。
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
uses
 
uses
Line 79: Line 80:
 
   LoginForm:=TDBConfigForm.Create(self);
 
   LoginForm:=TDBConfigForm.Create(self);
 
   try
 
   try
     // The test button on dbconfiggui will link to this procedure:
+
     // dbconfigguiのテストボタンはこのプロシージャにリンクされるだろう
     ... this links the callback in ''dbconfiggui.pas'' to the ConnectionTest function here...
+
     //ここで...これが''dbconfiggui.pas''の コールバックをConnectionTest functionにリンクさせる...
 
     LoginForm.ConnectionTestCallback:=@ConnectionTest;
 
     LoginForm.ConnectionTestCallback:=@ConnectionTest;
 
...
 
...
 
function TForm1.ConnectionTest(ChosenConfig: TDBConnectionConfig): boolean;
 
function TForm1.ConnectionTest(ChosenConfig: TDBConnectionConfig): boolean;
// Callback function that uses the info in dbconfiggui to test a connection
+
// コネクションを確かめるためにdbconfigguiの情報を使うコールバック関数
// and return the result of the test to dbconfiggui
+
// 確かめるために結果をdbconfigguiへ返す
 
var
 
var
   // Generic database connector...
+
   // 通常のデータベース接続...
 
   Conn: TSQLConnector;
 
   Conn: TSQLConnector;
 
begin
 
begin
Line 94: Line 95:
 
   Screen.Cursor:=crHourglass;
 
   Screen.Cursor:=crHourglass;
 
   try
 
   try
     // ...actual connector type is determined by this property.
+
     // ...このプロパティで決定される実際の接続タイプ。
     // Make sure the ChosenConfig.DBType string matches
+
     // ChosenConfig.DBType文字列がマッチすることを確かめること
     // the connectortype (e.g. see the string in the
+
     // コネクタタイプ(例えば、このコネクタにたいする
     // T*ConnectionDef.TypeName for that connector .
+
     // T*ConnectionDef.TypeNameをみること
 
     Conn.ConnectorType:=ChosenConfig.DBType;
 
     Conn.ConnectorType:=ChosenConfig.DBType;
 
     Conn.HostName:=ChosenConfig.DBHost;
 
     Conn.HostName:=ChosenConfig.DBHost;
Line 107: Line 108:
 
       result:=Conn.Connected;
 
       result:=Conn.Connected;
 
     except
 
     except
       // Result is already false
+
       // 結果はすでにfalse
 
     end;
 
     end;
 
     Conn.Close;
 
     Conn.Close;
Line 117: Line 118:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Finally, the code in ''dbconfiggui.pas'' that actually calls the callback is linked to the Test button. It tests if the callback function is assigned (to avoid crashes), for completeness also checks if there is a valid configuration object and then simply calls the callback function:
+
最後に、実際にコールバックを呼び出す ''dbconfiggui.pas'' 内のコードが [テスト] ボタンにリンクされます。 (クラッシュを避けるために) コールバック関数が割り当てられているかどうかをテストする。完全を期すために、有効な構成オブジェクトがあるかどうかもチェックし、単純にコールバック関数を呼び出す。
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 130: Line 131:
 
procedure TDBConfigForm.TestButtonClick(Sender: TObject);
 
procedure TDBConfigForm.TestButtonClick(Sender: TObject);
 
begin
 
begin
   // Call callback with settings, let it figure out if connection succeeded and
+
   // 設定を持つコールバックを呼び出す、接続が
   // get test result back
+
   // 成功したことを明らかにし、テスト結果を返す
 
   if assigned(FConnectionTestFunction) and assigned(FConnectionConfig) then
 
   if assigned(FConnectionTestFunction) and assigned(FConnectionConfig) then
 
     if FConnectionTestFunction(FConnectionConfig) then
 
     if FConnectionTestFunction(FConnectionConfig) then
Line 142: Line 143:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Additions/modifications ===
+
=== 追加・修正 ===
Possible additions/modifications for the login form:
+
ログインフォームの可能な追加/変更:
* Add command line arguments handling for dbconfig to preload suitable defaults, so the program can be used in batch scripts, shortcuts etc
+
* dbconfig のコマンド ライン引数処理を追加して適切なデフォルトをプリロードし、プログラムをバッチ スクリプトやショートカットなどで使用できるようにする。
* Add a "Select profile" combobox in the login form; use multiple profiles in the ini file that specify database type and connection details.
+
* ログインフォームに「プロファイルの選択」コンボボックスを追加する。 ini ファイル内でデータベースの種類と接続の詳細を指定する複数のプロファイルを使用する。
* Hide database type combobox when only one database type supported.
+
* 1 つのデータベース タイプのみがサポートされている場合は、データベース タイプ コンボボックスを非表示にする。
* Hide username/password when you're sure an embedded database is selected.
+
* 組み込みデータベースが選択されていることが確実な場合は、ユーザー名とパスワードを非表示にする。
* Add support for specifying port number, or instance name with MS SQL Server connector
+
* MS SQL Server コネクタを使用したポート番号またはインスタンス名の指定のサポートを追加。
* Add support for trusted authentication for dbs that support it (Firebird, MS SQL): disable ussername/password controls
+
* 信頼できる認証をサポートするデータベース (Firebird、MS SQL) にサポートを追加: ユーザー名/パスワードの制御を無効にする
* If an embedded database is selected but the file does not exist: show a confirmation request and create the database
+
* 組み込みデータベースを選択したがファイルが存在しない場合: 確認要求を表示し、データベースを作成する
* Create a command-line/TUI version of the login form (e.g. using the curses library) for command-line applictions
+
* コマンドライン アプリケーション用にログイン フォームのコマンドライン/TUI バージョンを作成する(たとえば、curses ライブラリを使用)
Updates to this article/the code warmly welcomed.
+
この記事やコードの更新は大歓迎する。
  
== Getting database data into normal controls ==
+
== データベース データを通常のコントロールに取り込む ==
{{Note|Before starting this section, please make sure you have set up the sample employee database as specified in [[SQLdb Tutorial0#Requirements]]}}
+
{{Note|このセクションを開始する前に、[[SQLdb Tutorial0/ja#必要条件|チュートリアル0の必要条件]] で指定されているようにサンプル従業員データベースが設定されていることを確認されたい。}}
  
In previous tutorials, data-bound controls were covered: special controls such as the [[TDBGrid]] that can bind its contents to a [[TDataSource]], get updates from that source and send user edits back.
+
前のチュートリアルでは、データ バインド コントロールについて説明した。[[TDBGrid]] などの特別なコントロールは、そのコンテンツを [[TDataSource]] にバインドし、そのソースから更新を取得し、ユーザーの編集内容を送り返すことができる。
  
It is also possible to programmatically retrieve database content and fill any kind of control (or variable) with that content. As an example, we will look at filling a stringgrid with some salary details for the sample employee database table.
+
プログラム的にデータベースの内容を取得し、その内容を任意の種類のコントロール (または変数) に入力することも可能である。 例として、サンプル従業員データベース テーブルの給与明細を stringgrid に入力する方法を見ていく。
  
On the main form, let's add a [[TStringGrid]] and retrieve the data (e.g. via a procedure ''LoadSalaryGrid'' called in the ''OnCreate'' event):
+
メイン フォームで [[TStringGrid]] を追加し、(例えば、''OnCreate'' イベントで呼び出されるプロシージャ ''LoadSalaryGrid'' 経由で)データを取得する 。
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
     // Load from DB
+
     // データベースから取得
 
     try
 
     try
 
       if not FConn.Connected then
 
       if not FConn.Connected then
Line 173: Line 174:
 
       end;
 
       end;
  
       // Lowest salary
+
       // 最低賃金
       // Note: we would like to only retrieve 1 row, but unfortunately the SQL
+
       // 注意: ここで1行だけ取得したいが、SQLは様々なデータベースシステムで異なる
       // used differs for various dbs. As we'll deal with db dependent SQL later
+
       // 後にチュートリアルで、データベースに依存したSQLを扱うが、ここではMS SQL:
      // in the tutorial, we leave this for now.
+
       // のMS SQL: 'select top 1 '...を用いておく
       // MS SQL: 'select top 1 '...
 
 
       FQuery.SQL.Text:='select ' +
 
       FQuery.SQL.Text:='select ' +
 
         '    e.first_name, ' +
 
         '    e.first_name, ' +
Line 184: Line 184:
 
         'from employee e ' +
 
         'from employee e ' +
 
         'order by e.salary asc ';
 
         'order by e.salary asc ';
         // ISO SQL+Firebird SQL: add
+
         // ISO SQL+Firebird SQL: では
         //'rows 1 '; here and below... won't work on e.g. PostgreSQL though
+
         //'rows 1 '; をここと以下に加えること。PostgreSQLでは動作しないが
 
       FTran.StartTransaction;
 
       FTran.StartTransaction;
 
       FQuery.Open;
 
       FQuery.Open;
Line 192: Line 192:
 
       SalaryGrid.Cells[3,1]:=FQuery.Fields[2].AsString;
 
       SalaryGrid.Cells[3,1]:=FQuery.Fields[2].AsString;
 
       FQuery.Close;
 
       FQuery.Close;
       // Always commit(retain) an opened transaction, even if only reading
+
       // 読み込むときでも、常にコミット(retain)すること
       // this will allow updates by others to be seen when reading again
+
       // これは読み直したときに他者によって更新することを可能にする
 
       FTran.Commit;
 
       FTran.Commit;
 
...
 
...
Line 206: Line 206:
 
     end;
 
     end;
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
注意すべき点: try..except を使用してデータベース エラーを検出する。 エラーが発生した場合にトランザクションをロールバックするのを忘れていることがわかるが、これは読者の演習として残されている。
  
Things to note: we catch database errors using try..except. You'll notice we forgot to roll back the transaction in case of errors - which is left as an exercise to the reader.
+
クエリ オブジェクトを ''Open'' し、それによって ''FQuery'' に ''SQL'' ステートメントを介してデータベースにクエリを実行するよう命ずる。 これが完了すると、データの最初の行が表示される。 私たちは単にデータが存在すると仮定する。 これは実際にはプログラミング エラーである。''FQuery.EOF'' が true (または ''FQuery.RecordCount'' が >0) であるかどうかを確認する方がよりまともだ。
  
We ''Open'' the query object, thereby asking ''FQuery'' to query the database via its ''SQL'' statement. Once this is done, we're on the first row of data. We simply assume there is data now; this is actually a programming error: it would be tidier to check for ''FQuery.EOF'' being true (or ''FQuery.RecordCount'' being >0).
+
次に、結果の最初の行からデータを取得する。 次の行に移動したい場合は、''FQuery.Next'' を使用するが、ここではその必要はない。 結果を stringgrid に入力し、リスト内の最低給与を示す。 最高の給与についても同様のアプローチがとれる。
  
Next, we retrieve the data from the first row of results. If we wanted to move to the next row, we'd use ''FQuery.Next'', but that is not necessary here. We put the results in the stringgrid, giving the lowest salary in the list. A similar approach can be taken for the highest salary.
+
上で述べたように、さまざまなデータベースがさまざまなバージョンの SQL をサポートしている(公式 ISO SQL 標準に加えて、またはこれに反して)。 幸いなことに、最終的に使用する DB に基づいてアプリケーションをカスタマイズできる。これは、従業員の給与の標準偏差を取得することで示される、例えば、PostgreSQL SQL では組み込みだが、例えば、Firebirdではデフォルトで利用できない。
  
== Adapting SQL for various databases ==
+
この、LoadSalaryGrid プロシージャではPostgreSQLのSQLを用い、コードを他の全てのデータベースに対して構築する。初めに、どのデータベースが読み込まれたか検知する。他の行に以下を加える:
As we noticed above, various databases support various versions of SQL (either in addition to or in contradiction to the official ISO SQL standards).
 
Fortunately, you can customize your application based on which DB it ends up using, which will be demonstrated by getting the standard deviation of the employees' salaries - built into e.g. PostgreSQL SQL but not available by default in e.g. Firebird.
 
  
In our LoadSalaryGrid procedure, we'll use the SQL for PostgreSQL and build a code solution for all other databases. First detect which database is loaded, below the other lines add:
 
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
   ...
 
   ...
 
   SalaryGrid.Cells[3,2]:=FQuery.Fields[2].AsString;
 
   SalaryGrid.Cells[3,2]:=FQuery.Fields[2].AsString;
 
   FQuery.Close;
 
   FQuery.Close;
   // Always commit(retain) an opened transaction, even if only reading
+
   // たとえ読み取り時でも、常にトランザクションをコミット(retain)すること
 
   FTran.Commit;
 
   FTran.Commit;
//end of existing code
+
//存在するコードの最後
  
 
   if FConn.ConnectorType = 'PostGreSQL' then
 
   if FConn.ConnectorType = 'PostGreSQL' then
 
   begin
 
   begin
     // For PostgreSQL, use a native SQL solution:
+
     // PostgreSQLに対しては、それ固有の SQL 解決策:
 
     FQuery.SQL.Text:='select stddev_pop(salary) from employee ';
 
     FQuery.SQL.Text:='select stddev_pop(salary) from employee ';
 
     FTran.StartTransaction;
 
     FTran.StartTransaction;
Line 235: Line 233:
 
       SalaryGrid.Cells[3,3]:=FQuery.Fields[0].AsString;
 
       SalaryGrid.Cells[3,3]:=FQuery.Fields[0].AsString;
 
     FQuery.Close;
 
     FQuery.Close;
     // Always commit(retain) an opened transaction, even if only reading
+
     // たとえ読み取り時でも、常にトランザクションをコミット(retain)すること
 
     FTran.Commit;
 
     FTran.Commit;
 
   end
 
   end
 
   else
 
   else
 
   begin
 
   begin
     // For other database, use the code approach:
+
     // 他のデータベースでは、このコードアプローチで:
     ....see below...
+
     ....以下参照...
 
   end;
 
   end;
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Notice the use of ''ConnectorType''; the string used must match exactly. We also properly check for empty results from the query (which might happen if the employee table is empty).
+
''ConnectorType'' の使用に注目すること。 使用される文字列は正確に一致する必要がある。 また、クエリからの空の結果も適切にチェックする (従業員テーブルが空の場合に発生する可能性がある)
  
... now let's implement a code-based solution for other databases that do not support standard deviation:
+
... ここで標準偏差をサポートしない他のデータベースのための、コードベースの解決策を実装する:
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
   // For other databases, use the code approach:
+
   // 他のデータベースでは、コードによるアプローチを行う:
   // 1. Get average of values
+
   // 1. 値の平均を求める
 
   FQuery.SQL.Text:='select avg(salary) from employee ';
 
   FQuery.SQL.Text:='select avg(salary) from employee ';
 
   FQuery.Open;
 
   FQuery.Open;
Line 259: Line 257:
 
     Average:=FQuery.Fields[0].AsFloat;
 
     Average:=FQuery.Fields[0].AsFloat;
 
     FQuery.Close;
 
     FQuery.Close;
     // 2. For each value, calculate the square of (value-average), and add it up
+
     // 2. それぞれの値に対し、平均値の二乗をとり、加え合わせると
 
     FQuery.SQL.Text:='select salary from employee where salary is not null ';
 
     FQuery.SQL.Text:='select salary from employee where salary is not null ';
 
     FQuery.Open;
 
     FQuery.Open;
Line 268: Line 266:
 
       FQuery.Next;
 
       FQuery.Next;
 
     end;
 
     end;
     // 3. Now calculate the average "squared difference" and take the square root
+
     // 3. 次に、平均「二乗差」を計算し、平方根を取得する。
     if Count>0 then //avoid division by 0
+
     if Count>0 then // 0での除算を防ぐ
 
       SalaryGrid.Cells[3,3]:=FloatToStr(Sqrt(DifferencesSquared/Count))
 
       SalaryGrid.Cells[3,3]:=FloatToStr(Sqrt(DifferencesSquared/Count))
 
     else
 
     else
Line 276: Line 274:
 
   FQuery.Close;
 
   FQuery.Close;
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
空のデータ(および0での除算など)を避けるために''FQuery.EOF''を用いていることに注意すること。このループがそのやり方を示している:
 
Note that we use ''FQuery.EOF'' to check for empty data (and avoid division by zero errors etc). The loop shows how to:
 
Note that we use ''FQuery.EOF'' to check for empty data (and avoid division by zero errors etc). The loop shows how to:
* retrieve a database value into a variable
+
* データベースの値を変数に代入する
* use ''FQuery.Next'' to move to the next record
+
* 次のレコードに移動するため''FQuery.Next'' を実行
* properly check if the query dataset has hit the last record, then stop retrieving data.
+
* クエリデータセットが最後のレコードに達したことを適切にチェックし、データ取得を停止する
  
The resulting screen should show something like this - note the use of a decimal comma - while your computer may show a decimal point depending on your locale:
+
結果の画面は以下のようになるだろう - 十進のコンマが用いられていることに気をつけられたい - コンピューターのロケールによってはこれが現れるかもしれない:
  
 
[[File:sqldbtutorial3mainform.png]]
 
[[File:sqldbtutorial3mainform.png]]
  
=== Revisiting our lowest/highest salary ===
+
=== 最低/最高給与を取得する ===
  
''This section gives some more useful details on SQL but is not required to work through for the rest of the tutorial''
+
''このセクションでは、SQL についてさらに役立つ詳細を説明するが、チュートリアルの残りの部分まで作業する必要はない''
  
Now we know how to deal with detecting various database connections, we can adjust the SQL that gets the lowest and highest salary as well to make use of db specific functionality.
+
これで、さまざまなデータベース接続の検出に対処する方法がわかったので、最低給与と最高給与を取得する SQL を調整して、データベース固有の機能を利用できるようになる。
  
An example: this would work for MS SQL Server by limiting the number of returned rows to just the first:
+
一例として: これは、返される行の数を最初の行のみに制限することで、MS SQL Server で機能するだろう:
 
<syntaxhighlight lang="SQL">
 
<syntaxhighlight lang="SQL">
 
select top 1  
 
select top 1  
Line 298: Line 297:
 
order by e.salary asc  
 
order by e.salary asc  
 
</syntaxhighlight>
 
</syntaxhighlight>
to get the lowest salary.
+
最低給与を求めるために。
  
This efficiently returns one record. Other databases use other syntax, such as the ISO ROWS 1. The diligent SQL student will soon learn not to miss out that important part and request entire large recordsets just for one required record!  
+
これにより効率的に 1 つのレコードが返される。 他のデータベースでは、ISO ROWS 1 などの他の構文が使用される。勤勉な SQL の学習者は、その重要な部分を見逃して、必要な 1 つのレコードだけのために大きなレコードセット全体を要求しないことをすぐに気づくだろう!
  
Let's briefly examine other ways to achieve the same thing, that are worth knowing.
+
同じことを達成するための、知っておく価値のある他の方法を簡単に検討してみよう。
  
Another way to retrieve the record(s) with the minimum salary would be :''
+
最低給与のレコードを取得する別の方法は次ようになるだろう:
 
<syntaxhighlight lang="SQL">SELECT e.first_name, e.last_name, e.salary FROM employee e WHERE e.salary=(SELECT min(salary) FROM employee)</syntaxhighlight>
 
<syntaxhighlight lang="SQL">SELECT e.first_name, e.last_name, e.salary FROM employee e WHERE e.salary=(SELECT min(salary) FROM employee)</syntaxhighlight>
  
SQL students would greatly benefit from researching Common Table Expressions.
+
SQL を学ぶものは、Common Table Expressionを研究することで大きな利益を得られるだろう。
  
A CTE allows a virtual temporary table to be used in a following expression, allowing you to clearly code some very complex queries that otherwise may not be possible. Knowing about CTEs will catapult you ahead of colleagues who have never heard of them! For example the above may be rewritten (example in Microsoft SQL Server syntax) as : ''
+
CTE を使用すると、次の式で仮想一時テーブルを使用できるようになり、他の方法では不可能な非常に複雑なクエリを明確にコーディングできるようになる。 CTE について知っていれば、それを聞いたことのない同僚よりも先を行くことができる。 たとえば、上記は次のように書き換えることができる (Microsoft SQL Server 構文の例):
 
<syntaxhighlight lang="SQL">WITH TheMinimum as
 
<syntaxhighlight lang="SQL">WITH TheMinimum as
 
(
 
(
Line 315: Line 314:
 
)
 
)
 
SELECT e.first_name, e.last_name, e.salary FROM Employee e WHERE e.salary=(SELECT MinimumPay FROM TheMinimum)</syntaxhighlight>
 
SELECT e.first_name, e.last_name, e.salary FROM Employee e WHERE e.salary=(SELECT MinimumPay FROM TheMinimum)</syntaxhighlight>
 
+
このような一時テーブルをいくつか連結させて、それぞれが前のテーブルの結果を使用することができる。 JOIN を使用してレコードセットをリンクすると、これらの仮想テーブルをデータベース内の実際のテーブルであるかのように扱うことができる。 また、ハードコードされたデータを使用した簡単なテストに非常に役立つ。これはデータベース接続なしで実行できる:
Several such temporary tables may be chained together, each using the results from the previous tables. You can treat these virtual tables as though they were real tables in the database, using JOINs to link recordsets together. And it can be very useful for quick tests using hardcoded data - this can be run without any database connection :
 
 
<syntaxhighlight lang="SQL">WITH TestEmployee as
 
<syntaxhighlight lang="SQL">WITH TestEmployee as
 
(
 
(
Line 331: Line 329:
 
SELECT e.first_name, e.last_name, e.salary FROM TestEmployee e WHERE e.salary=(SELECT MinimumPay FROM TheMinimum)</syntaxhighlight>
 
SELECT e.first_name, e.last_name, e.salary FROM TestEmployee e WHERE e.salary=(SELECT MinimumPay FROM TheMinimum)</syntaxhighlight>
  
You can end up with quite long strings for the code of such SQL queries, but it is only ''one'' query and may be called from anywhere where you are limited to a simple single expression - it can be useful to answer complex queries without resorting to functions or stored procedures.
+
このような SQL クエリのコードは非常に長い文字列になる可能性があるが、それは 1つのクエリにすぎず、単純な''1 つの''式に限定されているどこからでも呼び出すことができます。関数やストアドプロシージャに頼ることなく、複雑なクエリに答えるのに役立つ。
  
== Getting data out of normal controls into the database ==
+
== 通常のコントロールからデータベースへデータを取得する ==
Previously, we have seen:
+
 
* how to let SQLDB update the database with data-bound controls (earlier tutorials)
+
これまでに以下を見てきた:
* how to get data out of the database using queries (the section above)
+
* データ バインド コントロールを使用して SQLDB にデータベースを更新させる方法 (以前のチュートリアル)
 +
* クエリを使用してデータベースからデータを取得する方法 (上記のセクション)
 
   
 
   
You can also execute SQL to get arbitrary data back into the database via code. This allows you to use variables or controls that have no db aware equivalent such as sliders or custom controls to enter data into the database, at the expense of a bit more coding.
+
SQL を実行して、コード経由で任意のデータをデータベースに戻すこともできる。 これにより、多少のコーディングを犠牲にして、データベースに対応するスライダーやカスタム コントロールなどのデータベース対応のない変数やコントロールを使用してデータベースにデータを入力できるようになる。
 
 
As an example, we are going to allow the user to change the lowest and highest salary in the stringgrid.
 
  
For ease of editing, set the grid's ''Options''/''goEditing'' to true; then assign the procedure below to the OnValidate event for the grid, which will be called every time a user has finished updating the grid.
+
例として、ユーザーが stringgrid 内の最低給与と最高給与を変更できるようにする。
  
=== Parameterized queries ===
+
編集を容易にするために、グリッドの ''Options''/''goEditing'' を true に設定する。 次に、以下のプロシージャをグリッドの OnValidate イベントに割り当てる。このイベントは、ユーザーがグリッドの更新を完了するたびに呼び出される。
The following code also demonstrates how to use parameterized queries to avoid SQL injection, fiddling with quoting for string values, date formatting, etc.
 
  
As you can see in the code, you can name your parameters whatever you wish and prefix them with : in the SQL. In code, you can set/get their values by ''<somequery>.Params.ParamByName('<thename>').As'<variabletype>'; the code demonstrates ''.AsFloat'' and ''.AsString''.
+
=== パラメータクエリ ===
 +
次のコードは、パラメーター化されたクエリを使用して SQL インジェクション、文字列値の引用符の調整、日付の書式設定などを回避する方法も示している。
  
Parameterized queries are especially useful (and can be much faster) if you run the same query, only with different parameters, in a loop (think e.g. bulk loading of data).
+
コードにあるように、パラメーターに任意の名前を付けて、SQL 内で先頭に : を付けることができる。その値を''<somequery>.Params.ParamByName('<thename>').As'<variabletype>';で設定、取得できる。コードでは''.AsFloat'' and ''.AsString''を示す。
  
Continuing with our example: after having set up the query SQL and parameters, the transaction is started (and later on committed) as usual, then the query is run by calling ''ExecSQL'' (which does not return a result set; if the SQL statement were e.g. a SELECT or INSERT...RETURNING that does return data, you would use ''Open'' as in the examples above):
+
パラメーター化されたクエリは、同じクエリを異なるパラメーターのみを使用してループ内で実行する場合に特に便利である (データの一括読み込みなどを考えてみてほしい)
  
 +
例を続ける:クエリ SQL とパラメータを設定した後、通常どおりトランザクションが開始され (その後コミットされ)、その後、''ExecSQL'' を呼び出してクエリが実行される (結果セットは返されない。SQL ステートメントがデータを返す SELECT または INSERT...RETURNING である場合は、上記の例のように ''Open'' を使用する):
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
procedure TForm1.SalaryGridValidateEntry(sender: TObject; aCol, aRow: Integer;
 
procedure TForm1.SalaryGridValidateEntry(sender: TObject; aCol, aRow: Integer;
 
   const OldValue: string; var NewValue: String);
 
   const OldValue: string; var NewValue: String);
 
begin
 
begin
   // Only these cells have min and max salary:
+
   // 最低、最高給与を持つセルのみ:
 
   if (aCol=3) and ((aRow=1) or (aRow=2)) then
 
   if (aCol=3) and ((aRow=1) or (aRow=2)) then
 
   begin
 
   begin
     // Allow updates to min and max salary if positive numerical data is entered
+
     // もし正の数値データが入力された場合、最低、最高給与を更新することを可能にする
 
     if StrToFloatDef(NewValue,-1)>0 then
 
     if StrToFloatDef(NewValue,-1)>0 then
 
     begin
 
     begin
      // Storing the primary key in e.g. a hidden cell in the grid and using that in our
+
      // 主キーを次のように保存する。 グリッド内の非表示のセルとそれを使用します。
      // update query would be cleaner, but we can do it the hard way as well:
+
      // 更新クエリの方がクリーンだが、難しい方法でも実行できます:
 
       FQuery.SQL.Text:='update employee set salary=:newsalary '+
 
       FQuery.SQL.Text:='update employee set salary=:newsalary '+
 
         ' where first_name=:firstname and last_name=:lastname and salary=:salary ';
 
         ' where first_name=:firstname and last_name=:lastname and salary=:salary ';
Line 374: Line 372:
 
       FQuery.ExecSQL;
 
       FQuery.ExecSQL;
 
       FTran.Commit;
 
       FTran.Commit;
       LoadSalaryGrid; //reload standard deviation
+
       LoadSalaryGrid; //標準偏差を再び読み込み
 
     end
 
     end
 
     else
 
     else
 
     begin
 
     begin
       // Notify user that his input was wrong... he'll be wondering otherwise:
+
       // その入力が誤っていることを伝える... さもないと迷わせることになる:
 
       Showmessage('Invalid salary entered.');
 
       Showmessage('Invalid salary entered.');
 
       NewValue:=OldValue;
 
       NewValue:=OldValue;
Line 385: Line 383:
 
   else
 
   else
 
   begin
 
   begin
     // Silently discard edits to any other cells
+
     // 他のセルの編集を隠蔽して廃棄する
 
     NewValue:=OldValue;
 
     NewValue:=OldValue;
 
   end;
 
   end;
Line 391: Line 389:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Note how we forgot to add a try..except block to this code to nicely catch database errors and display a sensible error message. If you are running the Firebird sample EMPLOYEE database for this tutorial, try to change the salary to a very low value (say 1) and see what happens.
+
データベース エラーを適切に捕捉して適切なエラー メッセージを表示するために、このコードに try..except ブロックを追加するのを忘れていることに注意すること。 このチュートリアルで Firebird サンプル EMPLOYEE データベースを実行している場合は、給与を非常に低い値 (たとえば 1) に変更して、何が起こるかを確認されたい。
  
Finally, while this example showed an UPDATE SQL query, you could just as well run INSERT queries to insert new data programmatically. Also, you can use parameters in any kind of SQL query (SELECT, UPDATE, etc) as long as you use them for fields, not for table/view/procedure names.
+
最後に、この例では UPDATE SQL クエリを示したが、INSERT クエリを実行してプログラムで新しいデータを挿入することもできる。 また、テーブル/ビュー/プロシージャ名ではなく、フィールドにパラメータを使用する限り、あらゆる種類の SQL クエリ (SELECT、UPDATE など) でパラメータを使用できる。
  
== Summary ==
+
== まとめ ==
This tutorial explained:
+
このチュートリアルでは次のように説明した。
* how to code for multiple database types
+
* 複数のデータベースタイプをコード化する方法
* how to use a login form to decouple db access configuration from your program
+
* ログインフォームを使用してプログラムからデータベースアクセス構成を分離する方法
* how to retrieve and update data programmatically
+
* プログラムでデータを取得および更新する方法
  
== Code ==
+
== コード ==
Since November 2012, the code can be found in $(lazarusdir)examples/database/sqldbtutorial3
+
2012 年 11 月以降、コードは $(lazarusdir)examples/database/sqldbtutorial3 にある。
  
 
If you have an older version (e.g. Lazarus 1.0.2), you can also download the code via  
 
If you have an older version (e.g. Lazarus 1.0.2), you can also download the code via  
 
[http://svn.freepascal.org/svn/lazarus/trunk/examples/database/sqldbtutorial3/ the Lazarus SVN website]
 
[http://svn.freepascal.org/svn/lazarus/trunk/examples/database/sqldbtutorial3/ the Lazarus SVN website]
  
== See also ==
+
== 関連情報 ==
* [[SQLdb Tutorial0]]: Instructions for setting up sample tables/sample data for the tutorial series.
+
* [[SQLdb Tutorial1/ja]]: DB チュートリアルの第一の部分。データベースのデータを、どのように grid に表示させるかを学びます。
* [[SQLdb Tutorial1]]: First part of the DB tutorial series, showing how to set up a grid that shows database data
+
* [[SQLdb Tutorial2/ja]]: DB チュートリアルの第二の部分。データベースのデータの挿入や編集について学びます。
* [[SQLdb Tutorial2]]: Second part of the DB tutorial series, showing editing, inserting etc.
+
* [[SQLdb Tutorial3/ja]]: DB チュートリアルの第三の部分。複数のデータベースについてのプログラムやログインフォームの使い方を学びます。
* [[SQLdb Tutorial4]]: Fourth part of the DB tutorial series, showing how to use data modules
+
* [[SQLdb Tutorial4/ja]]: DB チュートリアルの第四の部分。どのようにデータモジュールを用いるかを学びます。
* [[Lazarus Database Overview]]: Information about the databases that Lazarus supports. Links to database-specific notes.
+
* [[Lazarus Database Overview/ja]]: Lazarus がサポートしているデータベースについての情報。 データベースごとの記述へのリンクを含みます。
* [[SQLdb Package]]: information about the SQLdb package
+
* [[SQLdb Package/ja]]: SQLdb パッケージについての情報
* [[SQLdb Programming Reference]]: an overview of the interaction of the SQLdb database components
+
* [[SQLdb Programming Reference/ja]]: SQLdb データベースコンポーネントの入出力の概要
* [http://www.freepascal.org/docs-html/fcl/sqldb/usingparams.html using parameters]
+
* [[SqlDBHowto/ja]]: SQLdb パッケージを用いることについての情報
* [[SqlDBHowto]]: information about using the SQLdb package
+
* [[Working With TSQLQuery/ja]]: TSQLQuery についての情報
* [[Working With TSQLQuery]]: information about TSQLQuery
 

Latest revision as of 12:58, 1 April 2024

English (en) français (fr) 日本語 (ja)

データベースのポータル

参照:

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

各種データベース

Advantage - MySQL - MSSQL - Postgres - Interbase - Firebird - Oracle - ODBC - Paradox - SQLite - dBASE - MS Access - Zeos

概要

このチュートリアルでは、次の方法を学ぶ

  • ログインフォームの使用など、アプリケーションを複数のデータベースに適したものにする
  • データベース データを (データベース コントロールではなく) 通常のコントロールに取得する
  • コントロールからデータを取得してデータベースに戻す
  • パラメータ化されたクエリを実行する。

マルチデータベースのサポート

任意のデータベースとログイン フォームを使用して、SQLDB がサポートする複数の異なるデータベース サーバー/組み込みライブラリをサポートできる。

利点:

  • ユーザー/プログラマは、任意の sqldb t*connection を動的に使用できるため、データベースを選択できる。

短所:

  • より複雑な SQL をさらに調整する必要がある可能性がある。 各データベースには独自の方言がある。 もちろん、データベース固有の SQL を呼び出すことも可能だが、これはメンテナンスの問題に発展する可能性がある。
  • T*connection 固有のプロパティ (Firebird 方言を設定するための TIBConnection.Dialect など) は使用できない。

マルチデータベース サポートを使用するには、TIBConnection などの特定の T*Connection の代わりに、TSQLConnector (TSQLConnection ではない) を使用する。これにより、(プログラムの実行中に) 特定の T*Connection が動的に選択される。 ConnectorTypeプロパティに基づいてT*Connectionを使用する:

uses
...
var
  Conn: TSQLConnector;
begin
  Conn:=TSQLConnector.Create(nil);
  try
     // ...実際のコネクタのタイプはこのプロパティによって決まる。
     // ChosenConfig.DBType 文字列が一致することを確認する
     // コネクタのタイプ (例:
     // そのコネクタの T*ConnectionDef.TypeName 。
    Conn.ConnectorType:=ChosenConfig.DBType;
    // 残りは通常通り:
    Conn.HostName:='DBSERVER';
    Conn.DatabaseName:='bigdatabase.fdb';
    Conn.UserName:='SYSDBA';
    Conn.Password:='masterkey';
    try
      Conn.Open;

ログインフォーム

SQLdb Tutorial1/ja で説明したように、ユーザーはアプリケーションにハードコードされた資格情報を使用するのではなく、フォーム (または安全に保存されている構成ファイル) を使用してデータベースにログインする必要がある。 セキュリティ上の考慮事項に加えて、データベース サーバーの情報が変更されるたびにアプリケーションを再コンパイルしなければならないのは、あまり良い考えではない。

dbconfiggui.pas では、ini ファイルが存在する場合はそこからデフォルト値を取得するログイン フォームをセットアップする。 これにより、エンタープライズ展開、いくつかの詳細 (データベース サーバ、データベース名) を入力してデフォルトの接続を設定できる。 このフォームでは、ユーザーは自分のユーザー名/パスワードを追加/編集し、次に進む前に接続をテストできる。

dbloginform.png

TDBConnectionConfig クラスを持つ別の dbconfig.pas ユニットを使用して、選択した接続の詳細を保存します。 このクラスは、ini ファイルからのデフォルト設定の読み取りをサポートしています。

これにより、GUI ログイン フォームなしでの使用 (例: 無人/バッチ操作の実行時) が可能になり、たとえば、Web サイトでの再利用が可能になる。

このTDBConnectionConfigクラスはログイン フォームにConfigプロパティとして表示されるため、メイン プログラムは設定フォームをモーダルに表示し、ユーザーによる [OK] クリックを検出して、フォーム設定を閉じる前に選択した設定を取得できる。

接続テストコールバック関数

ログイン フォームを柔軟に保つために (Zeos などの他のデータベース層で使用できる)、テスト セクションをコールバック関数として実装し、メイン プログラムで処理させる。

dbconfiggui.pas のログイン フォームの定義:

type
   TConnectionTestFunction = function(ChosenConfig: TDBConnectionConfig): オブジェクトのブール値。

メイン フォームは、構成フォームからのテスト リクエストを処理するために、この定義に一致する関数を実装する必要がある。

コールバック関数は、構成フォームによって渡された構成オブジェクトを受け取り、それを使用して、選択されたデータベース タイプとの接続を構築する。 その後、単純にサーバーへの接続を試行する。 成功した場合は関数の結果を true に設定し、そうでない場合は結果は false のままとなる。

存在しないサーバーへのデータベース接続の試行は長いタイムアウトになる可能性があるため、カーソルを砂時計アイコンに設定して待機する必要があることをユーザーに示す。

uses
...
dbconfig, dbconfiggui
...
procedure TForm1.FormCreate(Sender: TObject);
  LoginForm:=TDBConfigForm.Create(self);
  try
    // dbconfigguiのテストボタンはこのプロシージャにリンクされるだろう
    //ここで...これが''dbconfiggui.pas''の コールバックをConnectionTest functionにリンクさせる...
    LoginForm.ConnectionTestCallback:=@ConnectionTest;
...
function TForm1.ConnectionTest(ChosenConfig: TDBConnectionConfig): boolean;
// コネクションを確かめるためにdbconfigguiの情報を使うコールバック関数
// 確かめるために結果をdbconfigguiへ返す
var
  // 通常のデータベース接続...
  Conn: TSQLConnector;
begin
  result:=false;
  Conn:=TSQLConnector.Create(nil);
  Screen.Cursor:=crHourglass;
  try
    // ...このプロパティで決定される実際の接続タイプ。
    // ChosenConfig.DBType文字列がマッチすることを確かめること
    // コネクタタイプ(例えば、このコネクタにたいする
    // T*ConnectionDef.TypeNameをみること
    Conn.ConnectorType:=ChosenConfig.DBType;
    Conn.HostName:=ChosenConfig.DBHost;
    Conn.DatabaseName:=ChosenConfig.DBPath;
    Conn.UserName:=ChosenConfig.DBUser;
    Conn.Password:=ChosenConfig.DBPassword;
    try
      Conn.Open;
      result:=Conn.Connected;
    except
      // 結果はすでにfalse
    end;
    Conn.Close;
  finally
    Screen.Cursor:=crDefault;
    Conn.Free;
  end;
end;

最後に、実際にコールバックを呼び出す dbconfiggui.pas 内のコードが [テスト] ボタンにリンクされます。 (クラッシュを避けるために) コールバック関数が割り当てられているかどうかをテストする。完全を期すために、有効な構成オブジェクトがあるかどうかもチェックし、単純にコールバック関数を呼び出す。

...
TDBConfigForm = class(TForm)
...
  private
    FConnectionTestFunction: TConnectionTestFunction;
  public
    property ConnectionTestCallback: TConnectionTestFunction write FConnectionTestFunction;
...
procedure TDBConfigForm.TestButtonClick(Sender: TObject);
begin
  // 設定を持つコールバックを呼び出す、接続が
  // 成功したことを明らかにし、テスト結果を返す
  if assigned(FConnectionTestFunction) and assigned(FConnectionConfig) then
    if FConnectionTestFunction(FConnectionConfig) then
      showmessage('Connection test succeeded.')
    else
      showmessage('Connection test failed.')
  else
    showmessage('Error: connection test code has not been implemented.');
end;

追加・修正

ログインフォームの可能な追加/変更:

  • dbconfig のコマンド ライン引数処理を追加して適切なデフォルトをプリロードし、プログラムをバッチ スクリプトやショートカットなどで使用できるようにする。
  • ログインフォームに「プロファイルの選択」コンボボックスを追加する。 ini ファイル内でデータベースの種類と接続の詳細を指定する複数のプロファイルを使用する。
  • 1 つのデータベース タイプのみがサポートされている場合は、データベース タイプ コンボボックスを非表示にする。
  • 組み込みデータベースが選択されていることが確実な場合は、ユーザー名とパスワードを非表示にする。
  • MS SQL Server コネクタを使用したポート番号またはインスタンス名の指定のサポートを追加。
  • 信頼できる認証をサポートするデータベース (Firebird、MS SQL) にサポートを追加: ユーザー名/パスワードの制御を無効にする
  • 組み込みデータベースを選択したがファイルが存在しない場合: 確認要求を表示し、データベースを作成する
  • コマンドライン アプリケーション用にログイン フォームのコマンドライン/TUI バージョンを作成する(たとえば、curses ライブラリを使用)

この記事やコードの更新は大歓迎する。

データベース データを通常のコントロールに取り込む

Light bulb  Note: このセクションを開始する前に、チュートリアル0の必要条件 で指定されているようにサンプル従業員データベースが設定されていることを確認されたい。

前のチュートリアルでは、データ バインド コントロールについて説明した。TDBGrid などの特別なコントロールは、そのコンテンツを TDataSource にバインドし、そのソースから更新を取得し、ユーザーの編集内容を送り返すことができる。

プログラム的にデータベースの内容を取得し、その内容を任意の種類のコントロール (または変数) に入力することも可能である。 例として、サンプル従業員データベース テーブルの給与明細を stringgrid に入力する方法を見ていく。

メイン フォームで TStringGrid を追加し、(例えば、OnCreate イベントで呼び出されるプロシージャ LoadSalaryGrid 経由で)データを取得する 。

    // データベースから取得
    try
      if not FConn.Connected then
        FConn.Open;
      if not FConn.Connected then
      begin
        ShowMessage('Error connecting to the database. Aborting data loading.');
        exit;
      end;

      // 最低賃金
      // 注意: ここで1行だけ取得したいが、SQLは様々なデータベースシステムで異なる
      // 後にチュートリアルで、データベースに依存したSQLを扱うが、ここではMS SQL:
      // のMS SQL: 'select top 1 '...を用いておく
      FQuery.SQL.Text:='select ' +
        '    e.first_name, ' +
        '    e.last_name, ' +
        '    e.salary ' +
        'from employee e ' +
        'order by e.salary asc ';
        // ISO SQL+Firebird SQL: では
        //'rows 1 '; をここと以下に加えること。PostgreSQLでは動作しないが
      FTran.StartTransaction;
      FQuery.Open;
      SalaryGrid.Cells[1,1]:=FQuery.Fields[0].AsString;  // i.e. Cells[Col,Row]
      SalaryGrid.Cells[2,1]:=FQuery.Fields[1].AsString;
      SalaryGrid.Cells[3,1]:=FQuery.Fields[2].AsString;
      FQuery.Close;
      // 読み込むときでも、常にコミット(retain)すること
      // これは読み直したときに他者によって更新することを可能にする
      FTran.Commit;
...
      end;
    except
      on D: EDatabaseError do
      begin
        FTran.Rollback;
        MessageDlg('Error', 'A database error has occurred. Technical error message: ' +
          D.Message, mtError, [mbOK], 0);
      end;
    end;

注意すべき点: try..except を使用してデータベース エラーを検出する。 エラーが発生した場合にトランザクションをロールバックするのを忘れていることがわかるが、これは読者の演習として残されている。

クエリ オブジェクトを Open し、それによって FQuerySQL ステートメントを介してデータベースにクエリを実行するよう命ずる。 これが完了すると、データの最初の行が表示される。 私たちは単にデータが存在すると仮定する。 これは実際にはプログラミング エラーである。FQuery.EOF が true (または FQuery.RecordCount が >0) であるかどうかを確認する方がよりまともだ。

次に、結果の最初の行からデータを取得する。 次の行に移動したい場合は、FQuery.Next を使用するが、ここではその必要はない。 結果を stringgrid に入力し、リスト内の最低給与を示す。 最高の給与についても同様のアプローチがとれる。

上で述べたように、さまざまなデータベースがさまざまなバージョンの SQL をサポートしている(公式 ISO SQL 標準に加えて、またはこれに反して)。 幸いなことに、最終的に使用する DB に基づいてアプリケーションをカスタマイズできる。これは、従業員の給与の標準偏差を取得することで示される、例えば、PostgreSQL SQL では組み込みだが、例えば、Firebirdではデフォルトで利用できない。

この、LoadSalaryGrid プロシージャではPostgreSQLのSQLを用い、コードを他の全てのデータベースに対して構築する。初めに、どのデータベースが読み込まれたか検知する。他の行に以下を加える:

  ...
  SalaryGrid.Cells[3,2]:=FQuery.Fields[2].AsString;
  FQuery.Close;
  // たとえ読み取り時でも、常にトランザクションをコミット(retain)すること
  FTran.Commit;
//存在するコードの最後

  if FConn.ConnectorType = 'PostGreSQL' then
  begin
    // PostgreSQLに対しては、それ固有の SQL 解決策:
    FQuery.SQL.Text:='select stddev_pop(salary) from employee ';
    FTran.StartTransaction;
    FQuery.Open;
    if not FQuery.EOF then
      SalaryGrid.Cells[3,3]:=FQuery.Fields[0].AsString;
    FQuery.Close;
    //  たとえ読み取り時でも、常にトランザクションをコミット(retain)すること
    FTran.Commit;
  end
  else
  begin
    // 他のデータベースでは、このコードアプローチで:
    ....以下参照...
  end;

ConnectorType の使用に注目すること。 使用される文字列は正確に一致する必要がある。 また、クエリからの空の結果も適切にチェックする (従業員テーブルが空の場合に発生する可能性がある)。

... ここで標準偏差をサポートしない他のデータベースのための、コードベースの解決策を実装する:

  // 他のデータベースでは、コードによるアプローチを行う:
  // 1. 値の平均を求める
  FQuery.SQL.Text:='select avg(salary) from employee ';
  FQuery.Open;
  if FQuery.EOF then
    SalaryGrid.Cells[3,3]:='No data'
  else
  begin
    Average:=FQuery.Fields[0].AsFloat;
    FQuery.Close;
    // 2. それぞれの値に対し、平均値の二乗をとり、加え合わせると
    FQuery.SQL.Text:='select salary from employee where salary is not null ';
    FQuery.Open;
    while not FQuery.EOF do
    begin
      DifferencesSquared:=DifferencesSquared+Sqr(FQuery.Fields[0].AsFloat-Average);
      Count:=Count+1;
      FQuery.Next;
    end;
    // 3. 次に、平均「二乗差」を計算し、平方根を取得する。
    if Count>0 then // 0での除算を防ぐ
      SalaryGrid.Cells[3,3]:=FloatToStr(Sqrt(DifferencesSquared/Count))
    else
      SalaryGrid.Cells[3,3]:='No data'; 
  end;
  FQuery.Close;

空のデータ(および0での除算など)を避けるためにFQuery.EOFを用いていることに注意すること。このループがそのやり方を示している: Note that we use FQuery.EOF to check for empty data (and avoid division by zero errors etc). The loop shows how to:

  • データベースの値を変数に代入する
  • 次のレコードに移動するためFQuery.Next を実行
  • クエリデータセットが最後のレコードに達したことを適切にチェックし、データ取得を停止する

結果の画面は以下のようになるだろう - 十進のコンマが用いられていることに気をつけられたい - コンピューターのロケールによってはこれが現れるかもしれない:

sqldbtutorial3mainform.png

最低/最高給与を取得する

このセクションでは、SQL についてさらに役立つ詳細を説明するが、チュートリアルの残りの部分まで作業する必要はない

これで、さまざまなデータベース接続の検出に対処する方法がわかったので、最低給与と最高給与を取得する SQL を調整して、データベース固有の機能を利用できるようになる。

一例として: これは、返される行の数を最初の行のみに制限することで、MS SQL Server で機能するだろう:

select top 1 
e.first_name, e.last_name, e.salary 
from employee e
order by e.salary asc

最低給与を求めるために。

これにより効率的に 1 つのレコードが返される。 他のデータベースでは、ISO ROWS 1 などの他の構文が使用される。勤勉な SQL の学習者は、その重要な部分を見逃して、必要な 1 つのレコードだけのために大きなレコードセット全体を要求しないことをすぐに気づくだろう!。

同じことを達成するための、知っておく価値のある他の方法を簡単に検討してみよう。

最低給与のレコードを取得する別の方法は次ようになるだろう:

SELECT e.first_name, e.last_name, e.salary FROM employee e WHERE e.salary=(SELECT min(salary) FROM employee)

SQL を学ぶものは、Common Table Expressionを研究することで大きな利益を得られるだろう。

CTE を使用すると、次の式で仮想一時テーブルを使用できるようになり、他の方法では不可能な非常に複雑なクエリを明確にコーディングできるようになる。 CTE について知っていれば、それを聞いたことのない同僚よりも先を行くことができる。 たとえば、上記は次のように書き換えることができる (Microsoft SQL Server 構文の例):

WITH TheMinimum as
(
  SELECT min(salary) as MinimumPay FROM Employee
)
SELECT e.first_name, e.last_name, e.salary FROM Employee e WHERE e.salary=(SELECT MinimumPay FROM TheMinimum)

このような一時テーブルをいくつか連結させて、それぞれが前のテーブルの結果を使用することができる。 JOIN を使用してレコードセットをリンクすると、これらの仮想テーブルをデータベース内の実際のテーブルであるかのように扱うことができる。 また、ハードコードされたデータを使用した簡単なテストに非常に役立つ。これはデータベース接続なしで実行できる:

WITH TestEmployee as
(
  SELECT 'Fred' as first_name, 'Bloggs' as last_name, 10500 as salary
   UNION 
  SELECT 'Joe' as first_name, 'Public' as last_name, 10000 as salary
   UNION 
  SELECT 'Mike' as first_name, 'Mouse' as last_name, 11000 as salary
),
 TheMinimum as
(
  SELECT min(salary) as MinimumPay FROM TestEmployee
)
SELECT e.first_name, e.last_name, e.salary FROM TestEmployee e WHERE e.salary=(SELECT MinimumPay FROM TheMinimum)

このような SQL クエリのコードは非常に長い文字列になる可能性があるが、それは 1つのクエリにすぎず、単純な1 つの式に限定されているどこからでも呼び出すことができます。関数やストアドプロシージャに頼ることなく、複雑なクエリに答えるのに役立つ。

通常のコントロールからデータベースへデータを取得する

これまでに以下を見てきた:

  • データ バインド コントロールを使用して SQLDB にデータベースを更新させる方法 (以前のチュートリアル)
  • クエリを使用してデータベースからデータを取得する方法 (上記のセクション)

SQL を実行して、コード経由で任意のデータをデータベースに戻すこともできる。 これにより、多少のコーディングを犠牲にして、データベースに対応するスライダーやカスタム コントロールなどのデータベース対応のない変数やコントロールを使用してデータベースにデータを入力できるようになる。

例として、ユーザーが stringgrid 内の最低給与と最高給与を変更できるようにする。

編集を容易にするために、グリッドの Options/goEditing を true に設定する。 次に、以下のプロシージャをグリッドの OnValidate イベントに割り当てる。このイベントは、ユーザーがグリッドの更新を完了するたびに呼び出される。

パラメータクエリ

次のコードは、パラメーター化されたクエリを使用して SQL インジェクション、文字列値の引用符の調整、日付の書式設定などを回避する方法も示している。

コードにあるように、パラメーターに任意の名前を付けて、SQL 内で先頭に : を付けることができる。その値を<somequery>.Params.ParamByName('<thename>').As'<variabletype>';で設定、取得できる。コードでは.AsFloat and .AsStringを示す。

パラメーター化されたクエリは、同じクエリを異なるパラメーターのみを使用してループ内で実行する場合に特に便利である (データの一括読み込みなどを考えてみてほしい)。

例を続ける:クエリ SQL とパラメータを設定した後、通常どおりトランザクションが開始され (その後コミットされ)、その後、ExecSQL を呼び出してクエリが実行される (結果セットは返されない。SQL ステートメントがデータを返す SELECT または INSERT...RETURNING である場合は、上記の例のように Open を使用する):

procedure TForm1.SalaryGridValidateEntry(sender: TObject; aCol, aRow: Integer;
  const OldValue: string; var NewValue: String);
begin
  // 最低、最高給与を持つセルのみ:
  if (aCol=3) and ((aRow=1) or (aRow=2)) then
  begin
    // もし正の数値データが入力された場合、最低、最高給与を更新することを可能にする
    if StrToFloatDef(NewValue,-1)>0 then
    begin
       // 主キーを次のように保存する。 グリッド内の非表示のセルとそれを使用します。
       // 更新クエリの方がクリーンだが、難しい方法でも実行できます:
      FQuery.SQL.Text:='update employee set salary=:newsalary '+
        ' where first_name=:firstname and last_name=:lastname and salary=:salary ';
      FQuery.Params.ParamByName('newsalary').AsFloat:=StrToFloatDef(NewValue,0);
      FQuery.Params.ParamByName('firstname').AsString:=SalaryGrid.Cells[1,aRow];
      FQuery.Params.ParamByName('lastname').AsString:=SalaryGrid.Cells[2,aRow];
      FQuery.Params.ParamByName('salary').AsFloat:=StrToFloatDef(OldValue,0);
      FTran.StartTransaction;
      FQuery.ExecSQL;
      FTran.Commit;
      LoadSalaryGrid; //標準偏差を再び読み込み
    end
    else
    begin
      // その入力が誤っていることを伝える... さもないと迷わせることになる:
      Showmessage('Invalid salary entered.');
      NewValue:=OldValue;
    end;
  end
  else
  begin
    // 他のセルの編集を隠蔽して廃棄する
    NewValue:=OldValue;
  end;
end;

データベース エラーを適切に捕捉して適切なエラー メッセージを表示するために、このコードに try..except ブロックを追加するのを忘れていることに注意すること。 このチュートリアルで Firebird サンプル EMPLOYEE データベースを実行している場合は、給与を非常に低い値 (たとえば 1) に変更して、何が起こるかを確認されたい。

最後に、この例では UPDATE SQL クエリを示したが、INSERT クエリを実行してプログラムで新しいデータを挿入することもできる。 また、テーブル/ビュー/プロシージャ名ではなく、フィールドにパラメータを使用する限り、あらゆる種類の SQL クエリ (SELECT、UPDATE など) でパラメータを使用できる。

まとめ

このチュートリアルでは次のように説明した。

  • 複数のデータベースタイプをコード化する方法
  • ログインフォームを使用してプログラムからデータベースアクセス構成を分離する方法
  • プログラムでデータを取得および更新する方法

コード

2012 年 11 月以降、コードは $(lazarusdir)examples/database/sqldbtutorial3 にある。

If you have an older version (e.g. Lazarus 1.0.2), you can also download the code via the Lazarus SVN website

関連情報

  • SQLdb Tutorial1/ja: DB チュートリアルの第一の部分。データベースのデータを、どのように grid に表示させるかを学びます。
  • SQLdb Tutorial2/ja: DB チュートリアルの第二の部分。データベースのデータの挿入や編集について学びます。
  • SQLdb Tutorial3/ja: DB チュートリアルの第三の部分。複数のデータベースについてのプログラムやログインフォームの使い方を学びます。
  • SQLdb Tutorial4/ja: DB チュートリアルの第四の部分。どのようにデータモジュールを用いるかを学びます。
  • Lazarus Database Overview/ja: Lazarus がサポートしているデータベースについての情報。 データベースごとの記述へのリンクを含みます。
  • SQLdb Package/ja: SQLdb パッケージについての情報
  • SQLdb Programming Reference/ja: SQLdb データベースコンポーネントの入出力の概要
  • SqlDBHowto/ja: SQLdb パッケージを用いることについての情報
  • Working With TSQLQuery/ja: TSQLQuery についての情報