Difference between revisions of "MasterDetail/ja"

From Free Pascal wiki
Jump to navigationJump to search
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
== 概要 ==
 
== 概要 ==
リレーショナル データベースでは、マスター/ディテールの概念は 1 対多または 1:N とも呼ばれる。これは、テーブル/データセット内の 1 つのレコードが、別のテーブル/データセット内の多数のレコードに関連付けられる可能性があることを意味する。これは、あるデータセットのデータを使用して別のデータセットのデータをフィルター処理するモデルである。データのフィルタリングに使用されるデータセットはマスターと呼ばれ、フィルタリングされたデータセットはディテールと呼ばれる。たとえば、国には多くの都市があります。 国を選択し、そのすべての都市を表示したい場合は、マスター/ディテールに適した仕事である。国テーブルをマスター、都市テーブルをディテールにすることができる。フィルタリングが機能するのは、ディテール表にマスター表の識別子 (通常はマスター表の主キー) が含まれているためである。 したがって、このキーはマスターテーブルとディテールテーブルの両方に存在する。 詳細テーブルでは外部キーと呼ばれる。
+
リレーショナル データベースでは、マスター/ディテールの概念は 1 対多または 1:N とも呼ばれる。これは、テーブル/データセット内の 1 つのレコードが、別のテーブル/データセット内の多数のレコードに関連付けられる可能性があることを意味する。これは、あるデータセットのデータを使用して別のデータセットのデータをフィルター処理するモデルである。データのフィルタリングに使用されるデータセットはマスターと呼ばれ、フィルタリングされたデータセットはディテールと呼ばれる。たとえば、国には多くの都市があります。 国を選択し、そのすべての都市を表示したい場合は、マスター/ディテールに適した仕事である。国テーブルをマスター、都市テーブルをディテールにすることができる。フィルタリングが機能するのは、ディテール表にマスター表の識別子 (通常はマスター表の主キー) が含まれているためである。 したがって、このキーはマスターテーブルとディテールテーブルの両方に存在する。 ディテール テーブルでは外部キーと呼ばれる。
  
 
マスター/ディテール関係の典型的な例は次のとおりである:
 
マスター/ディテール関係の典型的な例は次のとおりである:
Line 8: Line 8:
 
以下の例では、customer テーブルと order テーブルを操作する。
 
以下の例では、customer テーブルと order テーブルを操作する。
  
== SQLDB implementation ==
+
== SQLDB 実装 ==
You can use the '''datasource''' property in the detail dataset to link to the master dataset.
+
詳細データセットの '''datasource''' プロパティを使用して、マスター データセットにリンクできる。
  
The approach below applies to both Lazarus and FPC code that uses the built in [[SQLdb Package]] database layer. It may also apply to other database layers (e.g. Zeos); please check with their documentation.
+
以下のアプローチは、組み込みの [[SQLdb Package]] データベース層を使用する Lazarus と FPC コードの両方に適用される。 他のデータベース層 (Zeo など) にも適用される場合がある。 ドキュメントを確認されたい。
  
Example from the EMPLOYEE database (as used in [[SQLdb Tutorial0]] and other tutorials):  
+
EMPLOYEE データベースの例 ([[SQLdb_Tutorial0/ja]] および他のチュートリアルで使用されているもの):
* a CUSTOMER table with an integer primary key CUST_NO and other fields
+
* 整数の主キー CUST_NO とその他のフィールドを持つ CUSTOMER テーブル
* a SALES table with a CUST_NO integer field that is a foreign key linking to the CUST_NO field in the CUSTOMER table
+
* CUSTOMER テーブルの CUST_NO フィールドにリンクする外部キーである CUST_NO 整数フィールドを持つ SALES テーブル
  
On the form:
+
フォーム上で:
* use one connection, 1 transaction, but 2 queries, 2 data sources
+
* 1 つの接続、1 つのトランザクションを使用するが、2 つのクエリ、2 つのデータ ソースを使用する
* a master query called qryCustomers selects from the CUSTOMER table
+
* qryCustomers というマスター クエリは CUSTOMER テーブルから選択する
* a detail query called qrySales that selects from the SALES table
+
* SALES テーブルから選択する qrySales というディテールクエリ
  
In the detail query qrySales:
+
ディテールクエリqrySalesで:
* set the database property as usual
+
* 通常通り、データベースプロパティを設定する
* set the datasource property to point to the master datasource
+
* データベースプロパティをマスターデータソースを示すように設定する
* in the query SQL, use a WHERE query to limit the select; use the detail fieldname and limit it to a parameter with the name of the field in the master table
+
* クエリ SQL では、WHERE クエリを使用して選択を制限する。 詳細フィールド名を使用し、マスター テーブル内のフィールドの名前を持つパラメーターに制限する。
 
<syntaxhighlight lang="SQL">SELECT * from SALES WHERE SALES.CUST_NO=:CUST_NO</syntaxhighlight>
 
<syntaxhighlight lang="SQL">SELECT * from SALES WHERE SALES.CUST_NO=:CUST_NO</syntaxhighlight>
In this case we use the SALES.CUST_NO field that happens to have the same field name as the master field CUST_NO, but that need not be the case.
+
この場合、たまたまマスター フィールド CUST_NO と同じフィールド名を持つ SALES.CUST_NO フィールドを使用するが、そうである必要はない。マスター データソースはマスター クエリ qryCustomers の現在のレコードを追跡するため、FPC はマスター/qryCustomers クエリの CUST_NO フィールドの現在の値への参照として CUST_NO パラメーターを確認できる。
As the master datasource keeps track of the current record for the master query qryCustomers, FPC can see the CUST_NO parameter as a reference to the current value of the CUST_NO field in the master/qryCustomers query.
 
  
If you want to use additional parameters in your query that should ''not'' be linked to the master query, make sure their <code>Bound</code> property is true before opening the master dataset. See [[doc:fcl/db/tparam.bound.html]]
+
マスター クエリにリンク ''すべきではない'' 追加のパラメーターをクエリで使用する場合は、マスター データセットを開く前に、その <code>Bound</code> プロパティが true であることを確認すること。[[doc:fcl/db/tparam.bound.html]]を参照のこと。
  
Make sure that the master query is open before the detail query so it can look up fields.
+
フィールドを検索できるように、ディテールクエリの前にマスター クエリが開いていることを確認すること。
  
=== Adding detail records with the right foreign key ===
+
=== 適切な外部キーを使用したディテールレコードの追加 ===
Although detail records scroll with master records, additional code is needed.
+
ディテールレコードはマスター レコードとともにスクロールしますが、追加のコードが必要である。
 
 
When adding new detail records, the SALES.CUST_NO field is still NULL unless we fill it. So we need to set up an AfterInsert event handler for qrySales:
 
  
 +
新しいディテールレコードを追加するとき、SALES.CUST_NO フィールドは、値を入力しない限り NULL のままである。したがって、qrySales の AfterInsert イベント ハンドラーを設定する必要がある:
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
procedure TForm1.qrySalesAfterInsert(DataSet: TDataSet);
 
procedure TForm1.qrySalesAfterInsert(DataSet: TDataSet);
Line 46: Line 44:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Using detail parameters that do not filter using master data ===
+
=== マスターデータを使用してフィルタリングしないディテールパラメータの使用 ===
By default, the values of all parameters in the detail dataset will be provided by the master dataset. Suppose you have another parameter in the detail dataset that you want to filter on independently:
+
デフォルトでは、詳細データセット内のすべてのパラメーターの値はマスター データセットによって提供される。 詳細データセットに個別にフィルター処理する別のパラメーターがあるとする:
  
 
Master Query:
 
Master Query:
Line 57: Line 55:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The solution is to tell SQLDB that the CREDITOVERDUE parameter should be "bound", using one of these methods:
+
解決策は、次のいずれかの方法を使用して、SQLDB に CREDITOVERDUE パラメータを「バインド」するように指示することである:
# Set a value for the parameter in the object inspector.
+
# オブジェクトインスペクタでパラメータの値を設定する。
# Mark the parameter as bound (to a value) before opening the master dataset: <code>qryDetail.Params.ParamByName('CREDITOVERDUE').Bound:=True;</code> See [[doc:fcl/db/tparam.bound.html|Bound property documentation]]
+
# マスター データセットを開く前に、パラメーターを (値に) バインド済みとしてマークする: <code>qryDetail.Params.ParamByName('CREDITOVERDUE').Bound:=True;</code> [[doc:fcl/db/tparam.bound.html|Bound property documentation]]参照。
# Set the parameter value before opening the master dataset:
+
# マスター データセットを開く前にパラメーター値を設定する:
 
<code>qryDetail.Params.ParamByName('CREDITOVERDUE').AsBoolean:=true;</code>
 
<code>qryDetail.Params.ParamByName('CREDITOVERDUE').AsBoolean:=true;</code>
  
=== Detail bookmarks ===
+
=== ディテール ブックマーク ===
Note: in TBufDataset and bufdataset descendents (such as SQLQuery), detail datasets are reloaded whenever the master dataset active record moves; therefore existing detail bookmarks, even if valid, will no longer point to the same detail record after moving.
+
注意: TBufDataset および bufdataset の派生 (SQLQuery など)、マスター データセットのアクティブ レコードが移動するたびに詳細データセットが再ロードされる; したがって、既存のディテール ブックマークは、たとえ有効であっても、移動後は同じディテール レコードを指さなくなる。
  
  
 
----
 
----
  
== '''Countries and cities example''' ==
+
== '''国と都市の例''' ==
  
This tutorial was made using SQLite but the principles are the same for other databases also.
+
このチュートリアルは SQLite を使用して作成されたが、原則は他のデータベースでも同じである。
  
In case you have no admin program for SQLite, I suggest SQLite Studio. http://sqlitestudio.pl
+
もしSQLiteの管理プログラムを持っていない場合、SQLite Studio http://sqlitestudio.pl を推奨する。
  
*First create a new SQLite database and save it as database.db3.
+
*初めに新しく SQLite データベースを作り、database.db3として保存する。
  
(If you use another database system, just create your tables as you are used to and use the appropriate components for the example).
+
(別のデータベース システムを使用する場合は、慣れているようにテーブルを作成し、例に適切なコンポーネントを使用するだけだ)
  
  
'''Now lets make two tables.'''
+
'''ここで、2つのテーブルを作る'''
  
The "countries" table:
+
「国」テーブル:
  
 
<syntaxhighlight lang="SQL">
 
<syntaxhighlight lang="SQL">
Line 89: Line 87:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
'''Add two countries to the table:'''
+
'''テーブルに2つの国を追加する:'''
 
*Sweden
 
*Sweden
 
*Norway
 
*Norway
  
Because COUNTRY_ID is autoincrement, each country is
+
Because COUNTRY_ID が自動的に加算されるので、それぞれの国は自動的にIDがふられる。
assigned an ID automatically.
 
  
'''Then make the "cities" table:'''
+
'''「都市」テーブルを作る:'''
 
<syntaxhighlight lang="SQL">
 
<syntaxhighlight lang="SQL">
 
CITY_ID (Primary key, integer, autoincrement)
 
CITY_ID (Primary key, integer, autoincrement)
Line 103: Line 100:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
'''Add three cities to this table:'''
+
'''3つの都市をこのテーブルに追加する:'''
*Stockholm with COUNTRY_ID 1
+
* Stockholm COUNTRY_ID 1
*Gothenburg with COUNTRY_ID 1
+
* Gothenburg COUNTRY_ID 1
*Oslo with COUNTRY_ID 2
+
* Oslo COUNTRY_ID 2
 
 
Since Sweden was added first in the empty table, it will have COUNTRY_ID 1 and Norway will have with COUNTRY_ID 2. For every city you add, you must supply its COUNTRY_ID because thats what links the tables together and make the master/detail concept possible.
 
  
 +
空のテーブルに最初にスウェーデンが追加されたため、スウェーデンは COUNTRY_ID 1 を持ち、ノルウェーは COUNTRY_ID 2 を持つ。追加する都市ごとに、COUNTRY_ID を指定する必要がある。COUNTRY_ID によってテーブルがリンクされ、マスター/詳細の概念が可能になるためである。
  
*Create a new project in Lazarus and save it as MasterDetail
+
* Lazarus で新しいプロジェクトを作成し、MasterDetail として保存する。
*Drop a TSQLite3Connection on the form. (SQLdb tab)
+
* TSQLite3Connection をフォームに配置する(SQLdb tab)
*In the DatabaseName property for TSQLite3Connection enter full path and filename for the SQLite Database file you just made.
+
* TSQLite3Connection の DatabaseName プロパティに、作成したばかりの SQLite データベース ファイルのフル パスとファイル名を入力する。
*Drop a TSQLTransaction on the form. (SQLdb tab)
+
* TSQLTransaction をフォームに配置する(SQLdb tab)
*Set its Database property to: SQLite3Connection1
+
* そのデータベース プロパティを: SQLite3Connection1へ。
*Return to TSQLite3Connection and set its Transaction property to SQLTransaction1
+
* TSQLite3Connectionに戻り、TransactionプロパティをSQLTransaction1に設定する。
*Set TSQLite3Connection.Connected to true
+
* TSQLite3Connection.Connected true にする。
  
'''You should now have a connection to the database and can proceed to the next step.'''
+
'''これでデータベースに接続できるようになり、次のステップに進むことができます。'''
  
*Drop a TSQLQuery on the form. (SQLdb tab)
+
* TSQLQueryをフォームに配置(SQLdb tab)
*Set its Database property to: SQLite3Connection1
+
* そのデータベースプロパティを: SQLite3Connection1にする
*Set its Transaction property to: SQLTransaction1
+
* Transaction プロパティを: SQLTransaction1にする。
*In the SQL statement, enter:  
+
* SQLステートメントに、以下を入力する:  
 
<syntaxhighlight lang="SQL">
 
<syntaxhighlight lang="SQL">
 
select * from countries  
 
select * from countries  
 
</syntaxhighlight>
 
</syntaxhighlight>
*Drop a TDataSource on the form. (Data Access tab)
+
* TDataSourceをフォームに配置(Data Access tab)
*Set its DataSet to: SQLQuery1
+
* このデータセットを: SQLQuery1にする。
  
*Drop a TDBGrid on the form (Data Controls tab)
+
* TDBGridをフォームに配置(Data Controls tab)
*Set its DataSource property to: DataSource1
+
* そのDataSourceプロパティを: DataSource1へ設定。
  
*Go to SQLQuery1 and set Active to true.
+
* SQLQuery1 に戻り、Active を trueにする。
  
'''The content of the countries table (master) should now be visible in DBGrid1.'''
+
'''国テーブル (マスター) の内容が DBGrid1 に表示されるようになる。'''
  
*Now proceed with the cities table (detail)
+
*次に都市テーブルに進みます(ディテール)
*Drop a second TSQLQuery on the form. (SQLdb tab)
+
*2つ目のTSQLQueryをフォームに配置(SQLdb tab)
*Set its Database property to: SQLite3Connection1
+
*そのデータベースプロパティを: SQLite3Connection1へ設定する。
*Set its Transaction property to: SQLTransaction1
+
*そのTransaction プロパティを: SQLTransaction1へ設定する。
*In the SQL statement, enter:
+
*SQL ステートメントに、以下を入力する:
 
   
 
   
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 149: Line 145:
 
where cities.COUNTRY_ID = :COUNTRY_ID
 
where cities.COUNTRY_ID = :COUNTRY_ID
 
</syntaxhighlight>
 
</syntaxhighlight>
The colon sign for :COUNTRY_ID just means that this is a variable parameter and that the value comes from somewhere else. It takes the value from the Master DataSource. So in our example the :COUNTRY_ID value is supplied by DataSource1, which is our Master DataSource. (The parameter name is not arbitrary: it is the relevant field name in the Master Table)
+
:COUNTRY_ID のコロン記号は、これが変数パラメーターであり、値が他の場所から取得されることを意味する。それはマスター データソースから値を取得する。したがって、この例では、:COUNTRY_ID 値はマスター データソースである DataSource1 によって提供される(パラメータ名は任意ではない。マスターテーブル内の関連するフィールド名である)
 
+
*そのDatasourceプロパティをDatasource1
*Set its Datasource property to Datasource1.
 
  
This does not yet allow us to add cities; to do this:
+
これではまだ都市を追加できない、これをする:
* Please use the information under #Adding detail records with the right foreign key  above.
+
* 上記の #正しい外部キーを使用した詳細レコードの追加 にある情報を使用のこと。
  
  
'''Continue with the Detail view:'''
+
'''ディテールビューを続行する:'''
  
*Drop a second TDataSource on the form.
+
*2つ目のTDataSourceをフォームに配置。
*Set its DataSet property to Query2 (detail query)
+
*そのDataSetプロパティをQuery2 (ディテール クエリ)にする。
*Drop a second DBGrid on the form (Detail grid)
+
*2つ目のDBGridをフォームに配置(ディテール グリッド)にする。
*Set its DataSource to DataSource2 (Detail DataSource)
+
*そのDataSourceを DataSource2 (ディテール ソース)にする。
  
  
'''Now get the data flowing into the grids. Set all these to active/connected:'''
+
'''次に、グリッドにデータを流し込む。 これらをすべてアクティブ/接続済みに設定する:'''
  
 
*DBConnection
 
*DBConnection
Line 172: Line 167:
 
*Query2
 
*Query2
  
 +
これで、DBGrid2にディテールデータがあるはずだ。
  
Now there should be detail data in the DBGrid2
+
覚えておくべきことは、(上記のように) 詳細クエリのデータソースをマスター データソースに設定する必要があるということである (この例では DataSource1)
 
 
 
 
A thing to remember is that (as above) the Detail Query's DataSource must be set to the Master DataSource. (DataSource1 in our example)
 
 
 
 
 
  
'''If you still didn't understand, look at this wiring scheme:'''
+
'''それでも理解できなかった場合は、次の連結スキームを見ること:'''
  
 
Master Query.DataSource := None
 
Master Query.DataSource := None
Line 194: Line 185:
 
Detail DBGrid.DataSource := Detail DataSource
 
Detail DBGrid.DataSource := Detail DataSource
  
== Delete all Detail records if Master record is deleted ==
+
== マスターレコードが削除された場合は、すべての詳細レコードを削除する ==
  
This is easy to do with Cascading Deletes. Once again, this is for SQLite, but other systems work similarly.
+
これは、カスケード削除を使用すると簡単に実行できる。 繰り返すが、これは SQLite 用だが、他のシステムでも同様に動作する。
  
* First turn on foreign keys for SQLite because it's off by default. Do this by adding FOREIGN_KEYS=ON to the Params property of TSQLite3Connection.
+
* SQLite の外部キーはデフォルトでオフになっているため、まず SQLite の外部キーをオンにする。 これを行うには、TSQLite3Connection の Params プロパティに FOREIGN_KEYS=ON を追加する。
  
* Then make sure the detail table is created so the foreign key (country_id) references the master table. In this case it is the cities table:
+
* 次に、外部キー (country_id) がマスター テーブルを参照するように詳細テーブルが作成されていることを確認する。この場合、それは都市テーブルである:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
   try
 
   try
     { Master table }
+
     { マスターテーブル }
 
     Conn.ExecuteDirect('CREATE TABLE IF NOT EXISTS countries (' +
 
     Conn.ExecuteDirect('CREATE TABLE IF NOT EXISTS countries (' +
 
                         ' country_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ' +
 
                         ' country_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ' +
 
                         ' country_name VARCHAR(30) ' +
 
                         ' country_name VARCHAR(30) ' +
 
                         ')');
 
                         ')');
     { Detail table }
+
     { ディテールテーブル }
 
     Conn.ExecuteDirect('CREATE TABLE IF NOT EXISTS cities (' +
 
     Conn.ExecuteDirect('CREATE TABLE IF NOT EXISTS cities (' +
 
                         ' city_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ' +
 
                         ' city_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ' +
Line 215: Line 206:
 
                         ' country_id INTEGER REFERENCES countries ON DELETE CASCADE' +
 
                         ' country_id INTEGER REFERENCES countries ON DELETE CASCADE' +
 
                         ')');
 
                         ')');
     { Sample data }
+
     { サンプルデータ }
 
     Conn.ExecuteDirect('INSERT INTO countries(country_name) VALUES(''Sweden'')');
 
     Conn.ExecuteDirect('INSERT INTO countries(country_name) VALUES(''Sweden'')');
 
     Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Stockholm'', 1)');
 
     Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Stockholm'', 1)');
Line 235: Line 226:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Thats it. From now on, all detail records are deleted automatically whenever the master record is deleted.
+
以上である。 今後は、マスター レコードが削除されるたびに、すべての詳細レコードが自動的に削除される。
 
 
  
Note: DON'T try to turn on foreign keys this way:
+
注意: この方法で外部キーを有効にしようとしないこと:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 244: Line 234:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
TSQLite3Connection starts a transaction before the first query is sent to the database, even in case of a ExecuteDirect. It is not possible to enable or disable foreign key constraints in the middle of a multi-statement transaction (when SQLite is not in autocommit mode). Attempting to do so does not return an error; it simply has no effect. Source: Ludob/SQLite documentation.
+
TSQLite3Connection は、ExecuteDirect の場合でも、最初のクエリがデータベースに送信される前にトランザクションを開始する。 複数ステートメントのトランザクションの途中では (SQLite が自動コミット モードではない場合)、外部キー制約を有効または無効にすることはできない。 そうしようとしてもエラーは返されない。 それは単に効果がない。 出典: Ludob/SQLite ドキュメント。
  
== One more example ==
+
== さらにもう1つの例 ==
There is a complete running example with source code on this forum topic:
+
このフォーラムのトピックには、ソース コードを含む完全な実行例がある。
  
How to Append a new line and save it in DBGrid?
+
新しい行を追加して DBGrid に保存する方法
  
 
http://forum.lazarus.freepascal.org/index.php/topic,42088.msg293305.html#msg293305
 
http://forum.lazarus.freepascal.org/index.php/topic,42088.msg293305.html#msg293305
  
== See also ==
+
== 以下も参照のこと ==
 
* http://www.freepascal.org/docs-html/fcl/sqldb/usingparams.html Documentation on using parameters, including implementation for master/detail
 
* http://www.freepascal.org/docs-html/fcl/sqldb/usingparams.html Documentation on using parameters, including implementation for master/detail
 
* http://docwiki.embarcadero.com/RADStudio/XE3/en/Establishing_Master-detail_Relationships_Using_Parameters The documentation for the equivalent Delphi feature
 
* http://docwiki.embarcadero.com/RADStudio/XE3/en/Establishing_Master-detail_Relationships_Using_Parameters The documentation for the equivalent Delphi feature

Latest revision as of 11:24, 31 March 2024

概要

リレーショナル データベースでは、マスター/ディテールの概念は 1 対多または 1:N とも呼ばれる。これは、テーブル/データセット内の 1 つのレコードが、別のテーブル/データセット内の多数のレコードに関連付けられる可能性があることを意味する。これは、あるデータセットのデータを使用して別のデータセットのデータをフィルター処理するモデルである。データのフィルタリングに使用されるデータセットはマスターと呼ばれ、フィルタリングされたデータセットはディテールと呼ばれる。たとえば、国には多くの都市があります。 国を選択し、そのすべての都市を表示したい場合は、マスター/ディテールに適した仕事である。国テーブルをマスター、都市テーブルをディテールにすることができる。フィルタリングが機能するのは、ディテール表にマスター表の識別子 (通常はマスター表の主キー) が含まれているためである。 したがって、このキーはマスターテーブルとディテールテーブルの両方に存在する。 ディテール テーブルでは外部キーと呼ばれる。

マスター/ディテール関係の典型的な例は次のとおりである:

  • 顧客と注文
  • 注文した商品を含む注文

以下の例では、customer テーブルと order テーブルを操作する。

SQLDB 実装

詳細データセットの datasource プロパティを使用して、マスター データセットにリンクできる。

以下のアプローチは、組み込みの SQLdb Package データベース層を使用する Lazarus と FPC コードの両方に適用される。 他のデータベース層 (Zeo など) にも適用される場合がある。 ドキュメントを確認されたい。

EMPLOYEE データベースの例 (SQLdb_Tutorial0/ja および他のチュートリアルで使用されているもの):

  • 整数の主キー CUST_NO とその他のフィールドを持つ CUSTOMER テーブル
  • CUSTOMER テーブルの CUST_NO フィールドにリンクする外部キーである CUST_NO 整数フィールドを持つ SALES テーブル

フォーム上で:

  • 1 つの接続、1 つのトランザクションを使用するが、2 つのクエリ、2 つのデータ ソースを使用する
  • qryCustomers というマスター クエリは CUSTOMER テーブルから選択する
  • SALES テーブルから選択する qrySales というディテールクエリ

ディテールクエリqrySalesで:

  • 通常通り、データベースプロパティを設定する
  • データベースプロパティをマスターデータソースを示すように設定する
  • クエリ SQL では、WHERE クエリを使用して選択を制限する。 詳細フィールド名を使用し、マスター テーブル内のフィールドの名前を持つパラメーターに制限する。
SELECT * from SALES WHERE SALES.CUST_NO=:CUST_NO

この場合、たまたまマスター フィールド CUST_NO と同じフィールド名を持つ SALES.CUST_NO フィールドを使用するが、そうである必要はない。マスター データソースはマスター クエリ qryCustomers の現在のレコードを追跡するため、FPC はマスター/qryCustomers クエリの CUST_NO フィールドの現在の値への参照として CUST_NO パラメーターを確認できる。

マスター クエリにリンク すべきではない 追加のパラメーターをクエリで使用する場合は、マスター データセットを開く前に、その Bound プロパティが true であることを確認すること。doc:fcl/db/tparam.bound.htmlを参照のこと。

フィールドを検索できるように、ディテールクエリの前にマスター クエリが開いていることを確認すること。

適切な外部キーを使用したディテールレコードの追加

ディテールレコードはマスター レコードとともにスクロールしますが、追加のコードが必要である。

新しいディテールレコードを追加するとき、SALES.CUST_NO フィールドは、値を入力しない限り NULL のままである。したがって、qrySales の AfterInsert イベント ハンドラーを設定する必要がある:

procedure TForm1.qrySalesAfterInsert(DataSet: TDataSet);
begin
  DataSet.FieldByName('CUST_NO').AsInteger := qryCustomers.FieldByName('CUST_NO').AsInteger;
end;

マスターデータを使用してフィルタリングしないディテールパラメータの使用

デフォルトでは、詳細データセット内のすべてのパラメーターの値はマスター データセットによって提供される。 詳細データセットに個別にフィルター処理する別のパラメーターがあるとする:

Master Query: Same as above

Detail query:

SELECT * FROM SALES WHERE SALES.CUST_NO=:CUST_NO AND OVERDUE=:CREDITOVERDUE

解決策は、次のいずれかの方法を使用して、SQLDB に CREDITOVERDUE パラメータを「バインド」するように指示することである:

  1. オブジェクトインスペクタでパラメータの値を設定する。
  2. マスター データセットを開く前に、パラメーターを (値に) バインド済みとしてマークする: qryDetail.Params.ParamByName('CREDITOVERDUE').Bound:=True; Bound property documentation参照。
  3. マスター データセットを開く前にパラメーター値を設定する:

qryDetail.Params.ParamByName('CREDITOVERDUE').AsBoolean:=true;

ディテール ブックマーク

注意: TBufDataset および bufdataset の派生 (SQLQuery など)、マスター データセットのアクティブ レコードが移動するたびに詳細データセットが再ロードされる; したがって、既存のディテール ブックマークは、たとえ有効であっても、移動後は同じディテール レコードを指さなくなる。



国と都市の例

このチュートリアルは SQLite を使用して作成されたが、原則は他のデータベースでも同じである。

もしSQLiteの管理プログラムを持っていない場合、SQLite Studio http://sqlitestudio.pl を推奨する。

  • 初めに新しく SQLite データベースを作り、database.db3として保存する。

(別のデータベース システムを使用する場合は、慣れているようにテーブルを作成し、例に適切なコンポーネントを使用するだけだ)。


ここで、2つのテーブルを作る

「国」テーブル:

COUNTRY_ID (Primary key, integer, autoincrement)
COUNTRY_NAME (VARCHAR(50), not null)

テーブルに2つの国を追加する:

  • Sweden
  • Norway

Because COUNTRY_ID が自動的に加算されるので、それぞれの国は自動的にIDがふられる。

「都市」テーブルを作る:

CITY_ID (Primary key, integer, autoincrement)
CITY_NAME (VARCHAR(50), not null)
COUNTRY_ID (integer, not null) (foreign key)

3つの都市をこのテーブルに追加する:

  • Stockholm COUNTRY_ID 1
  • Gothenburg COUNTRY_ID 1
  • Oslo COUNTRY_ID 2

空のテーブルに最初にスウェーデンが追加されたため、スウェーデンは COUNTRY_ID 1 を持ち、ノルウェーは COUNTRY_ID 2 を持つ。追加する都市ごとに、COUNTRY_ID を指定する必要がある。COUNTRY_ID によってテーブルがリンクされ、マスター/詳細の概念が可能になるためである。

  • Lazarus で新しいプロジェクトを作成し、MasterDetail として保存する。
  • TSQLite3Connection をフォームに配置する(SQLdb tab)。
  • TSQLite3Connection の DatabaseName プロパティに、作成したばかりの SQLite データベース ファイルのフル パスとファイル名を入力する。
  • TSQLTransaction をフォームに配置する(SQLdb tab)。
  • そのデータベース プロパティを: SQLite3Connection1へ。
  • TSQLite3Connectionに戻り、TransactionプロパティをSQLTransaction1に設定する。
  • TSQLite3Connection.Connected を true にする。

これでデータベースに接続できるようになり、次のステップに進むことができます。

  • TSQLQueryをフォームに配置(SQLdb tab)。
  • そのデータベースプロパティを: SQLite3Connection1にする
  • Transaction プロパティを: SQLTransaction1にする。
  • SQLステートメントに、以下を入力する:
select * from countries
  • TDataSourceをフォームに配置(Data Access tab)。
  • このデータセットを: SQLQuery1にする。
  • TDBGridをフォームに配置(Data Controls tab)。
  • そのDataSourceプロパティを: DataSource1へ設定。
  • SQLQuery1 に戻り、Active を trueにする。

国テーブル (マスター) の内容が DBGrid1 に表示されるようになる。

  • 次に都市テーブルに進みます(ディテール)
  • 2つ目のTSQLQueryをフォームに配置(SQLdb tab)。
  • そのデータベースプロパティを: SQLite3Connection1へ設定する。
  • そのTransaction プロパティを: SQLTransaction1へ設定する。
  • SQL ステートメントに、以下を入力する:
select *
from cities
where cities.COUNTRY_ID = :COUNTRY_ID
COUNTRY_ID のコロン記号は、これが変数パラメーターであり、値が他の場所から取得されることを意味する。それはマスター データソースから値を取得する。したがって、この例では、:COUNTRY_ID 値はマスター データソースである DataSource1 によって提供される(パラメータ名は任意ではない。マスターテーブル内の関連するフィールド名である)。
  • そのDatasourceプロパティをDatasource1

これではまだ都市を追加できない、これをする:

  • 上記の #正しい外部キーを使用した詳細レコードの追加 にある情報を使用のこと。


ディテールビューを続行する:

  • 2つ目のTDataSourceをフォームに配置。
  • そのDataSetプロパティをQuery2 (ディテール クエリ)にする。
  • 2つ目のDBGridをフォームに配置(ディテール グリッド)にする。
  • そのDataSourceを DataSource2 (ディテール ソース)にする。


次に、グリッドにデータを流し込む。 これらをすべてアクティブ/接続済みに設定する:

  • DBConnection
  • Transaction
  • Query1
  • Query2

これで、DBGrid2にディテールデータがあるはずだ。

覚えておくべきことは、(上記のように) 詳細クエリのデータソースをマスター データソースに設定する必要があるということである (この例では DataSource1)。

それでも理解できなかった場合は、次の連結スキームを見ること:

Master Query.DataSource := None

Master DataSource.Dataset := Master Query

Master DBGrid.DataSource := Master DataSource

Detail Query.DataSource := Master DataSource

Detail DataSource.Dataset := Detail Query

Detail DBGrid.DataSource := Detail DataSource

マスターレコードが削除された場合は、すべての詳細レコードを削除する

これは、カスケード削除を使用すると簡単に実行できる。 繰り返すが、これは SQLite 用だが、他のシステムでも同様に動作する。

  • SQLite の外部キーはデフォルトでオフになっているため、まず SQLite の外部キーをオンにする。 これを行うには、TSQLite3Connection の Params プロパティに FOREIGN_KEYS=ON を追加する。
  • 次に、外部キー (country_id) がマスター テーブルを参照するように詳細テーブルが作成されていることを確認する。この場合、それは都市テーブルである:
  try
    { マスターテーブル }
    Conn.ExecuteDirect('CREATE TABLE IF NOT EXISTS countries (' +
                        ' country_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ' +
                        ' country_name VARCHAR(30) ' +
                        ')');
    { ディテールテーブル }
    Conn.ExecuteDirect('CREATE TABLE IF NOT EXISTS cities (' +
                        ' city_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ' +
                        ' city_name VARCHAR(30), ' +
                        ' country_id INTEGER REFERENCES countries ON DELETE CASCADE' +
                        ')');
    { サンプルデータ }
    Conn.ExecuteDirect('INSERT INTO countries(country_name) VALUES(''Sweden'')');
    Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Stockholm'', 1)');
    Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Malmo'', 1)');

    Conn.ExecuteDirect('INSERT INTO countries(country_name) VALUES(''Norway'')');
    Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Oslo'', 2)');
    Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Bergen'', 2)');

    Conn.Transaction.Commit;
    Conn.Close;
  except
    on E:Exception do
    begin
      TX.Rollback;
      ShowMessage( E.Message );
    end;
  end;

以上である。 今後は、マスター レコードが削除されるたびに、すべての詳細レコードが自動的に削除される。

注意: この方法で外部キーを有効にしようとしないこと:

Conn.ExecuteDirect('PRAGMA foreign_keys = ON');

TSQLite3Connection は、ExecuteDirect の場合でも、最初のクエリがデータベースに送信される前にトランザクションを開始する。 複数ステートメントのトランザクションの途中では (SQLite が自動コミット モードではない場合)、外部キー制約を有効または無効にすることはできない。 そうしようとしてもエラーは返されない。 それは単に効果がない。 出典: Ludob/SQLite ドキュメント。

さらにもう1つの例

このフォーラムのトピックには、ソース コードを含む完全な実行例がある。

新しい行を追加して DBGrid に保存する方法

http://forum.lazarus.freepascal.org/index.php/topic,42088.msg293305.html#msg293305

以下も参照のこと