Sign in Join
Wiki copytemporary

MasterDetail/ja

English · 日本語

概要

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

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

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

SQLDB 実装

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

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

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

フォーム上で:

ディテールクエリqrySalesで:

sql
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 イベント ハンドラーを設定する必要がある:

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

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

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

Master Query: Same as above

Detail query:

sql
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 を推奨する。

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

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

「国」テーブル:

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

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

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

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

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

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

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

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

sql
select * from countries

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

pascal
select *
from cities
where cities.COUNTRY_ID = :COUNTRY_ID

COUNTRY_ID のコロン記号は、これが変数パラメーターであり、値が他の場所から取得されることを意味する。それはマスター データソースから値を取得する。したがって、この例では、:COUNTRY_ID 値はマスター データソースである DataSource1 によって提供される(パラメータ名は任意ではない。マスターテーブル内の関連するフィールド名である)。

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

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

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

これで、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 用だが、他のシステムでも同様に動作する。

pascal
  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;

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

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

pascal
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

以下も参照のこと


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.

Revision 1, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.