MasterDetail/ja: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: MasterDetail/ja
| Old | New | Line | |
|---|---|---|---|
| 1 | + | ## 概要 | |
| 2 | + | ||
| 3 | + | リレーショナル データベースでは、マスター/ディテールの概念は 1 対多または 1:N とも呼ばれる。これは、テーブル/データセット内の 1 つのレコードが、別のテーブル/データセット内の多数のレコードに関連付けられる可能性があることを意味する。これは、あるデータセットのデータを使用して別のデータセットのデータをフィルター処理するモデルである。データのフィルタリングに使用されるデータセットはマスターと呼ばれ、フィルタリングされたデータセットはディテールと呼ばれる。たとえば、国には多くの都市があります。 国を選択し、そのすべての都市を表示したい場合は、マスター/ディテールに適した仕事である。国テーブルをマスター、都市テーブルをディテールにすることができる。フィルタリングが機能するのは、ディテール表にマスター表の識別子 (通常はマスター表の主キー) が含まれているためである。 したがって、このキーはマスターテーブルとディテールテーブルの両方に存在する。 ディテール テーブルでは外部キーと呼ばれる。 | |
| 4 | + | ||
| 5 | + | マスター/ディテール関係の典型的な例は次のとおりである: | |
| 6 | + | ||
| 7 | + | - 顧客と注文 | |
| 8 | + | - 注文した商品を含む注文 | |
| 9 | + | ||
| 10 | + | 以下の例では、customer テーブルと order テーブルを操作する。 | |
| 11 | + | ||
| 12 | + | ## SQLDB 実装 | |
| 13 | + | ||
| 14 | + | 詳細データセットの **datasource** プロパティを使用して、マスター データセットにリンクできる。 | |
| 15 | + | ||
| 16 | + | 以下のアプローチは、組み込みの [[SQLdb Package]] データベース層を使用する Lazarus と FPC コードの両方に適用される。 他のデータベース層 (Zeo など) にも適用される場合がある。 ドキュメントを確認されたい。 | |
| 17 | + | ||
| 18 | + | EMPLOYEE データベースの例 ([[SQLdb Tutorial0/ja]] および他のチュートリアルで使用されているもの): | |
| 19 | + | ||
| 20 | + | - 整数の主キー CUST_NO とその他のフィールドを持つ CUSTOMER テーブル | |
| 21 | + | - CUSTOMER テーブルの CUST_NO フィールドにリンクする外部キーである CUST_NO 整数フィールドを持つ SALES テーブル | |
| 22 | + | ||
| 23 | + | フォーム上で: | |
| 24 | + | ||
| 25 | + | - 1 つの接続、1 つのトランザクションを使用するが、2 つのクエリ、2 つのデータ ソースを使用する | |
| 26 | + | - qryCustomers というマスター クエリは CUSTOMER テーブルから選択する | |
| 27 | + | - SALES テーブルから選択する qrySales というディテールクエリ | |
| 28 | + | ||
| 29 | + | ディテールクエリqrySalesで: | |
| 30 | + | ||
| 31 | + | - 通常通り、データベースプロパティを設定する | |
| 32 | + | - データベースプロパティをマスターデータソースを示すように設定する | |
| 33 | + | - クエリ SQL では、WHERE クエリを使用して選択を制限する。 詳細フィールド名を使用し、マスター テーブル内のフィールドの名前を持つパラメーターに制限する。 | |
| 34 | + | ||
| 35 | + | ``` SQL | |
| 36 | + | SELECT * from SALES WHERE SALES.CUST_NO=:CUST_NO | |
| 37 | + | ``` | |
| 38 | + | ||
| 39 | + | この場合、たまたまマスター フィールド CUST_NO と同じフィールド名を持つ SALES.CUST_NO フィールドを使用するが、そうである必要はない。マスター データソースはマスター クエリ qryCustomers の現在のレコードを追跡するため、FPC はマスター/qryCustomers クエリの CUST_NO フィールドの現在の値への参照として CUST_NO パラメーターを確認できる。 | |
| 40 | + | ||
| 41 | + | マスター クエリにリンク *すべきではない* 追加のパラメーターをクエリで使用する場合は、マスター データセットを開く前に、その `Bound` プロパティが true であることを確認すること。[[doc:fcl/db/tparam.bound.html|doc:fcl/db/tparam.bound.htmlを]]参照のこと。 | |
| 42 | + | ||
| 43 | + | フィールドを検索できるように、ディテールクエリの前にマスター クエリが開いていることを確認すること。 | |
| 44 | + | ||
| 45 | + | ### 適切な外部キーを使用したディテールレコードの追加 | |
| 46 | + | ||
| 47 | + | ディテールレコードはマスター レコードとともにスクロールしますが、追加のコードが必要である。 | |
| 48 | + | ||
| 49 | + | 新しいディテールレコードを追加するとき、SALES.CUST_NO フィールドは、値を入力しない限り NULL のままである。したがって、qrySales の AfterInsert イベント ハンドラーを設定する必要がある: | |
| 50 | + | ||
| 51 | + | ``` pascal | |
| 52 | + | procedure TForm1.qrySalesAfterInsert(DataSet: TDataSet); | |
| 53 | + | begin | |
| 54 | + | DataSet.FieldByName('CUST_NO').AsInteger := qryCustomers.FieldByName('CUST_NO').AsInteger; | |
| 55 | + | end; | |
| 56 | + | ``` | |
| 57 | + | ||
| 58 | + | ### マスターデータを使用してフィルタリングしないディテールパラメータの使用 | |
| 59 | + | ||
| 60 | + | デフォルトでは、詳細データセット内のすべてのパラメーターの値はマスター データセットによって提供される。 詳細データセットに個別にフィルター処理する別のパラメーターがあるとする: | |
| 61 | + | ||
| 62 | + | Master Query: Same as above | |
| 63 | + | ||
| 64 | + | Detail query: | |
| 65 | + | ||
| 66 | + | ``` SQL | |
| 67 | + | SELECT * FROM SALES WHERE SALES.CUST_NO=:CUST_NO AND OVERDUE=:CREDITOVERDUE | |
| 68 | + | ``` | |
| 69 | + | ||
| 70 | + | 解決策は、次のいずれかの方法を使用して、SQLDB に CREDITOVERDUE パラメータを「バインド」するように指示することである: | |
| 71 | + | ||
| 72 | + | 1. オブジェクトインスペクタでパラメータの値を設定する。 | |
| 73 | + | 2. マスター データセットを開く前に、パラメーターを (値に) バインド済みとしてマークする: `qryDetail.Params.ParamByName('CREDITOVERDUE').Bound:=True;` [[doc:fcl/db/tparam.bound.html|Bound property documentation]]参照。 | |
| 74 | + | 3. マスター データセットを開く前にパラメーター値を設定する: | |
| 75 | + | ||
| 76 | + | `qryDetail.Params.ParamByName('CREDITOVERDUE').AsBoolean:=true;` | |
| 77 | + | ||
| 78 | + | ### ディテール ブックマーク | |
| 79 | + | ||
| 80 | + | 注意: TBufDataset および bufdataset の派生 (SQLQuery など)、マスター データセットのアクティブ レコードが移動するたびに詳細データセットが再ロードされる; したがって、既存のディテール ブックマークは、たとえ有効であっても、移動後は同じディテール レコードを指さなくなる。 | |
| 81 | + | ||
| 82 | + | ------------------------------------------------------------------------ | |
| 83 | + | ||
| 84 | + | ## **国と都市の例** | |
| 85 | + | ||
| 86 | + | このチュートリアルは SQLite を使用して作成されたが、原則は他のデータベースでも同じである。 | |
| 87 | + | ||
| 88 | + | もしSQLiteの管理プログラムを持っていない場合、SQLite Studio <http://sqlitestudio.pl> を推奨する。 | |
| 89 | + | ||
| 90 | + | - 初めに新しく SQLite データベースを作り、database.db3として保存する。 | |
| 91 | + | ||
| 92 | + | (別のデータベース システムを使用する場合は、慣れているようにテーブルを作成し、例に適切なコンポーネントを使用するだけだ)。 | |
| 93 | + | ||
| 94 | + | **ここで、2つのテーブルを作る** | |
| 95 | + | ||
| 96 | + | 「国」テーブル: | |
| 97 | + | ||
| 98 | + | ``` SQL | |
| 99 | + | COUNTRY_ID (Primary key, integer, autoincrement) | |
| 100 | + | COUNTRY_NAME (VARCHAR(50), not null) | |
| 101 | + | ``` | |
| 102 | + | ||
| 103 | + | **テーブルに2つの国を追加する:** | |
| 104 | + | ||
| 105 | + | - Sweden | |
| 106 | + | - Norway | |
| 107 | + | ||
| 108 | + | Because COUNTRY_ID が自動的に加算されるので、それぞれの国は自動的にIDがふられる。 | |
| 109 | + | ||
| 110 | + | **「都市」テーブルを作る:** | |
| 111 | + | ||
| 112 | + | ``` SQL | |
| 113 | + | CITY_ID (Primary key, integer, autoincrement) | |
| 114 | + | CITY_NAME (VARCHAR(50), not null) | |
| 115 | + | COUNTRY_ID (integer, not null) (foreign key) | |
| 116 | + | ``` | |
| 117 | + | ||
| 118 | + | **3つの都市をこのテーブルに追加する:** | |
| 119 | + | ||
| 120 | + | - Stockholm COUNTRY_ID 1 | |
| 121 | + | - Gothenburg COUNTRY_ID 1 | |
| 122 | + | - Oslo COUNTRY_ID 2 | |
| 123 | + | ||
| 124 | + | 空のテーブルに最初にスウェーデンが追加されたため、スウェーデンは COUNTRY_ID 1 を持ち、ノルウェーは COUNTRY_ID 2 を持つ。追加する都市ごとに、COUNTRY_ID を指定する必要がある。COUNTRY_ID によってテーブルがリンクされ、マスター/詳細の概念が可能になるためである。 | |
| 125 | + | ||
| 126 | + | - Lazarus で新しいプロジェクトを作成し、MasterDetail として保存する。 | |
| 127 | + | - TSQLite3Connection をフォームに配置する(SQLdb tab)。 | |
| 128 | + | - TSQLite3Connection の DatabaseName プロパティに、作成したばかりの SQLite データベース ファイルのフル パスとファイル名を入力する。 | |
| 129 | + | - TSQLTransaction をフォームに配置する(SQLdb tab)。 | |
| 130 | + | - そのデータベース プロパティを: SQLite3Connection1へ。 | |
| 131 | + | - TSQLite3Connectionに戻り、TransactionプロパティをSQLTransaction1に設定する。 | |
| 132 | + | - TSQLite3Connection.Connected を true にする。 | |
| 133 | + | ||
| 134 | + | **これでデータベースに接続できるようになり、次のステップに進むことができます。** | |
| 135 | + | ||
| 136 | + | - TSQLQueryをフォームに配置(SQLdb tab)。 | |
| 137 | + | - そのデータベースプロパティを: SQLite3Connection1にする | |
| 138 | + | - Transaction プロパティを: SQLTransaction1にする。 | |
| 139 | + | - SQLステートメントに、以下を入力する: | |
| 140 | + | ||
| 141 | + | ``` SQL | |
| 142 | + | select * from countries | |
| 143 | + | ``` | |
| 144 | + | ||
| 145 | + | - TDataSourceをフォームに配置(Data Access tab)。 | |
| 146 | + | - このデータセットを: SQLQuery1にする。 | |
| 147 | + | ||
| 148 | + | - TDBGridをフォームに配置(Data Controls tab)。 | |
| 149 | + | - そのDataSourceプロパティを: DataSource1へ設定。 | |
| 150 | + | ||
| 151 | + | - SQLQuery1 に戻り、Active を trueにする。 | |
| 152 | + | ||
| 153 | + | **国テーブル (マスター) の内容が DBGrid1 に表示されるようになる。** | |
| 154 | + | ||
| 155 | + | - 次に都市テーブルに進みます(ディテール) | |
| 156 | + | - 2つ目のTSQLQueryをフォームに配置(SQLdb tab)。 | |
| 157 | + | - そのデータベースプロパティを: SQLite3Connection1へ設定する。 | |
| 158 | + | - そのTransaction プロパティを: SQLTransaction1へ設定する。 | |
| 159 | + | - SQL ステートメントに、以下を入力する: | |
| 160 | + | ||
| 161 | + | ``` pascal | |
| 162 | + | select * | |
| 163 | + | from cities | |
| 164 | + | where cities.COUNTRY_ID = :COUNTRY_ID | |
| 165 | + | ``` | |
| 166 | + | ||
| 167 | + | ||
| 168 | + | COUNTRY_ID のコロン記号は、これが変数パラメーターであり、値が他の場所から取得されることを意味する。それはマスター データソースから値を取得する。したがって、この例では、:COUNTRY_ID 値はマスター データソースである DataSource1 によって提供される(パラメータ名は任意ではない。マスターテーブル内の関連するフィールド名である)。 | |
| 169 | + | ||
| 170 | + | - そのDatasourceプロパティをDatasource1 | |
| 171 | + | ||
| 172 | + | これではまだ都市を追加できない、これをする: | |
| 173 | + | ||
| 174 | + | - 上記の \#正しい外部キーを使用した詳細レコードの追加 にある情報を使用のこと。 | |
| 175 | + | ||
| 176 | + | **ディテールビューを続行する:** | |
| 177 | + | ||
| 178 | + | - 2つ目のTDataSourceをフォームに配置。 | |
| 179 | + | - そのDataSetプロパティをQuery2 (ディテール クエリ)にする。 | |
| 180 | + | - 2つ目のDBGridをフォームに配置(ディテール グリッド)にする。 | |
| 181 | + | - そのDataSourceを DataSource2 (ディテール ソース)にする。 | |
| 182 | + | ||
| 183 | + | **次に、グリッドにデータを流し込む。 これらをすべてアクティブ/接続済みに設定する:** | |
| 184 | + | ||
| 185 | + | - DBConnection | |
| 186 | + | - Transaction | |
| 187 | + | - Query1 | |
| 188 | + | - Query2 | |
| 189 | + | ||
| 190 | + | これで、DBGrid2にディテールデータがあるはずだ。 | |
| 191 | + | ||
| 192 | + | 覚えておくべきことは、(上記のように) 詳細クエリのデータソースをマスター データソースに設定する必要があるということである (この例では DataSource1)。 | |
| 193 | + | ||
| 194 | + | **それでも理解できなかった場合は、次の連結スキームを見ること:** | |
| 195 | + | ||
| 196 | + | Master Query.DataSource := None | |
| 197 | + | ||
| 198 | + | Master DataSource.Dataset := Master Query | |
| 199 | + | ||
| 200 | + | Master DBGrid.DataSource := Master DataSource | |
| 201 | + | ||
| 202 | + | Detail Query.DataSource := Master DataSource | |
| 203 | + | ||
| 204 | + | Detail DataSource.Dataset := Detail Query | |
| 205 | + | ||
| 206 | + | Detail DBGrid.DataSource := Detail DataSource | |
| 207 | + | ||
| 208 | + | ## マスターレコードが削除された場合は、すべての詳細レコードを削除する | |
| 209 | + | ||
| 210 | + | これは、カスケード削除を使用すると簡単に実行できる。 繰り返すが、これは SQLite 用だが、他のシステムでも同様に動作する。 | |
| 211 | + | ||
| 212 | + | - SQLite の外部キーはデフォルトでオフになっているため、まず SQLite の外部キーをオンにする。 これを行うには、TSQLite3Connection の Params プロパティに FOREIGN_KEYS=ON を追加する。 | |
| 213 | + | ||
| 214 | + | - 次に、外部キー (country_id) がマスター テーブルを参照するように詳細テーブルが作成されていることを確認する。この場合、それは都市テーブルである: | |
| 215 | + | ||
| 216 | + | ``` pascal | |
| 217 | + | try | |
| 218 | + | { マスターテーブル } | |
| 219 | + | Conn.ExecuteDirect('CREATE TABLE IF NOT EXISTS countries (' + | |
| 220 | + | ' country_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ' + | |
| 221 | + | ' country_name VARCHAR(30) ' + | |
| 222 | + | ')'); | |
| 223 | + | { ディテールテーブル } | |
| 224 | + | Conn.ExecuteDirect('CREATE TABLE IF NOT EXISTS cities (' + | |
| 225 | + | ' city_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, ' + | |
| 226 | + | ' city_name VARCHAR(30), ' + | |
| 227 | + | ' country_id INTEGER REFERENCES countries ON DELETE CASCADE' + | |
| 228 | + | ')'); | |
| 229 | + | { サンプルデータ } | |
| 230 | + | Conn.ExecuteDirect('INSERT INTO countries(country_name) VALUES(''Sweden'')'); | |
| 231 | + | Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Stockholm'', 1)'); | |
| 232 | + | Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Malmo'', 1)'); | |
| 233 | + | ||
| 234 | + | Conn.ExecuteDirect('INSERT INTO countries(country_name) VALUES(''Norway'')'); | |
| 235 | + | Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Oslo'', 2)'); | |
| 236 | + | Conn.ExecuteDirect('INSERT INTO cities(city_name, country_id) VALUES(''Bergen'', 2)'); | |
| 237 | + | ||
| 238 | + | Conn.Transaction.Commit; | |
| 239 | + | Conn.Close; | |
| 240 | + | except | |
| 241 | + | on E:Exception do | |
| 242 | + | begin | |
| 243 | + | TX.Rollback; | |
| 244 | + | ShowMessage( E.Message ); | |
| 245 | + | end; | |
| 246 | + | end; | |
| 247 | + | ``` | |
| 248 | + | ||
| 249 | + | 以上である。 今後は、マスター レコードが削除されるたびに、すべての詳細レコードが自動的に削除される。 | |
| 250 | + | ||
| 251 | + | 注意: この方法で外部キーを有効にしようとしないこと: | |
| 252 | + | ||
| 253 | + | ``` pascal | |
| 254 | + | Conn.ExecuteDirect('PRAGMA foreign_keys = ON'); | |
| 255 | + | ``` | |
| 256 | + | ||
| 257 | + | TSQLite3Connection は、ExecuteDirect の場合でも、最初のクエリがデータベースに送信される前にトランザクションを開始する。 複数ステートメントのトランザクションの途中では (SQLite が自動コミット モードではない場合)、外部キー制約を有効または無効にすることはできない。 そうしようとしてもエラーは返されない。 それは単に効果がない。 出典: Ludob/SQLite ドキュメント。 | |
| 258 | + | ||
| 259 | + | ## さらにもう1つの例 | |
| 260 | + | ||
| 261 | + | このフォーラムのトピックには、ソース コードを含む完全な実行例がある。 | |
| 262 | + | ||
| 263 | + | 新しい行を追加して DBGrid に保存する方法 | |
| 264 | + | ||
| 265 | + | <http://forum.lazarus.freepascal.org/index.php/topic,42088.msg293305.html#msg293305> | |
| 266 | + | ||
| 267 | + | ## 以下も参照のこと | |
| 268 | + | ||
| 269 | + | - <http://www.freepascal.org/docs-html/fcl/sqldb/usingparams.html> Documentation on using parameters, including implementation for master/detail | |
| 270 | + | - <http://docwiki.embarcadero.com/RADStudio/XE3/en/Establishing_Master-detail_Relationships_Using_Parameters> The documentation for the equivalent Delphi feature | |
| 271 | + | - <http://forum.codecall.net/topic/62683-master-detail-datasets/> | |
| 272 | + | - [[Lazarus Tdbf Tutorial|Lazarus Tdbf Tutorial#Master table relations]] Example of using master/detail with the TDBF components | |
| 273 | + | - [[SQLdb Tutorial0]] - Set up instructions for sample data for the subsequent database tutorials. Has master/detail tables | |
| 274 | + | ||
| 275 | + | --- | |
| 276 | + | *From the Free Pascal wiki, page "MasterDetail/ja", as it stood on 2024-03-31 after a change by Ariben, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* | |
| 277 | + | Category: Databases | |
| 278 | + | Category: FPC | |
| 279 | + | Category: Lazarus | |
| 280 | + | Category: Tutorials |
