10

この質問で回答されているように: Cardinality in PostgreSQL、カーディナリティは Constraint を使用して適用されます

カーディナリティ ルールは、1 対多、多対多などの関係の許容数を定義します。多対多は結合テーブルを使用して実現され、1 対多は FOREIGN KEY を使用して実現されます。

しかし、1 対 1 または多 (1 対 1+) の関係を実装するにはどうすればよいでしょうか。これは質問するのと同じです: PostgreSQL で最小限のカーディナリティを適用するにはどうすればよいですか?

実際の状況は、人 (ユーザーまたは顧客など) によって提供されなければならない (ただし、複数の場合もある) 住所 (または電話番号) を保存する必要がある場合です。

編集:

上記の状況は、一般的な問題の特殊なケース (カーディナリティ 1) です。一般的な問題は次のとおりです。任意の数のカーディナリティを強制する方法は?

jug が回答したように、最小カーディナリティが 1 の場合、 null 以外のFOREIGN KEY 参照を回避策として使用できます。また、多くの中からデフォルトを選択する追加機能も提供します。

しかし、クリケットのチームとその選手の間の関係の別の状況を考えてみましょう。チームとしての資格を得るには、すべてのチームに最低 11 人のプレーヤーが必要です。ここでは、最小カーディナリティは 11 です。

同様に、コースと学校の学生との関係では、すべての学生が少なくとも 5 つのコースに登録する必要があり、すべてのコースに最低 10 人の学生が必要です。

4

4 に答える 4

3

FOREIGN KEY制約のみを使用してそのような規則を適用する方法はありません。

1)1つの方法は、テーブル間の循環参照を許可することです(「デフォルト」列、ジャグが推奨)。これにより、鶏が先か卵が先かという問題が発生し、管理が困難になります。遅延可能な制約を使用する必要があります。さらに、このオプションは一部の DBMS では使用できません。もう 1 つの欠点は、サッカー チームの場合、11 個の「デフォルト」列を追加する必要があることです (そして、鶏と卵が 11 個ある問題に対処する必要があります)。

2) 別のオプションは、トリガーを使用することです。

3) 別のオプションは、2 つのテーブル間でデータベース レベルの制約を使用することです。そのような機能を持つ DBMS があるかどうかはわかりません。典型的なUNIQUEPRIMARYおよびFOREIGN KEY制約に加えて、ほとんどの DBMS には行レベルの制約のみがあり、制限があります (サブクエリなしなど)。

4) もう 1 つのオプションは、関連する 2 つのテーブルにのみアクセスできる適切な INSERT、UPDATE、および DELETE プロシージャを作成することによって、そのような規則を適用し、これらの規則に従って整合性を適用することです。これはより良いアプローチです(私の意見では)。

5) 実装がより簡単なもう 1 つのオプションは、標準の外部キー制約を使用して、1 対多の関係を強制し、実際に 11 人以上のプレーヤーがいるチームを示すビューを持つことです。これはもちろん、要求したルールを実際に適用しないことを意味します。しかし、そうではない可能性もあります (可能性は高いと言えます)。たとえば、サッカー選手が事故で亡くなった場合、チームはトーナメントでプレーできなくなりますが、それでもチームです。したがって、ゲームをプレイできる Team (ベース テーブル) と ProperTeam (ビュー) の 2 つのエンティティを定義できます。例:

CREATE VIEW AS ProperTeam
( SELECT *
  FROM Team
  WHERE ( SELECT COUNT(*)
          FROM Player
          WHERE Player.TeamId = Team.TeamId
        ) >= 11
) 

オプション 1 と 2 はどちらかというと「ごちゃごちゃ」しているように見えますが、これは単なる個人的な意見であり、多くの人はトリガーが好きです。

オプション 5 で制約を実際に強制しない (「ごまかす」ことができない) 場合を除き、オプション 4 を選択します。

于 2012-02-13T13:08:58.843 に答える
3

There's no way to specify this using a CHECK constraint, so I think the best approach is a trigger:

http://www.postgresql.org/docs/9.1/static/sql-createtrigger.html
http://www.postgresql.org/docs/9.1/static/plpgsql-trigger.html

You'd end up with something like (I haven't tested it or anything):

CREATE TRIGGER at_least_one before INSERT, UPDATE, DELETE ON the_one_table  FOR EACH ROW EXECUTE PROCEDURE check_at_least_one();

CREATE OR REPLACE FUNCTION check_at_least_one() RETURNS trigger AS $$
    BEGIN
    nmany := select count(*) from the_many_table where the_many_table.the_one_id=NEW.id;   
    IF nmany > 0 THEN 
        RETURN NEW;
    END IF;
    RETURN NULL;
END;
于 2012-02-12T15:25:41.260 に答える
2

1つのテーブルアドレスにアドレスがある場合は、提供する必要のあるアドレスへのnull以外の外部キー参照である列「default_address」(テーブルcustomers内を定義できます

人、住所、場合によっては注文(-item)を参照することで、オプションの多対多の関係を提供するテーブル配送がある場合は、合体を使用して、外部結合で取得したアドレスのNULLを上書きできます。 (顧客は注文を内部で結合します)およびshipping_addresses (出荷アドレスで結合するビュー)。しかし、アドレスのnull以外のコンポーネントの数が異なる可能性がある問題を防ぐために、Stephane Faroultは、彼の(強くお勧めします!)本The Art of SQLで、「隠しソートキー」手法を使用することを推奨しています(customers_with_default_addressがビュー結合であると想定)「default_address」を使用するアドレスを持つ顧客:

select *
  from (select 1 as sortkey,
               line_1,
               line_2,
               city,
               state,
               postal_code,
               country
        from shipping_addresses
          where customer_id = ?
        union
        select 2 as sortkey,
               line_1,
               line_2,
               city,
               state,
               postal_code,
               country
        from customers_with_default_address
          where customer_id = ?
        order by 1) actual_shipping_address
      limit 1
于 2012-02-12T18:42:44.220 に答える
1

私にとってうまくいき、妥当な量のコーディングが必要なアプローチは次のとおりです(チーム/プレーヤーの質問に翻訳されています):

  • 延期可能な外部キー制約を作成して、「各プレイヤーは1 つのチームを持つ」関係を強制します。
  • テーブル チームにトリガーを 1 つだけ作成し、少なくとも n 人のプレイヤーがチームに所属していることを確認します。AdamKG が指摘したように、カーディナリティが尊重されない場合、トリガーは例外をスローする必要があります。

これにより制約が強制されますが、副作用として、これにより、新しいプレーヤーのチームをエンコードする方法が 1 つしか許可されなくなります (つまり、キー違反として拒否することはありません)。

start transaction;
set constraints all deferred;
insert player_1 with teamPK --teamPK is not yet in table team, will be below
...
insert player_n with teamPK
insert teamPK --this fires the trigger which is successful.
commit transaction; --this fires the foreign key deferred check, successful.

(自動インクリメントされた ID を使用するのではなく)テーブル team に実際に行を挿入する前に teamPK を知ることができるように、一意のチーム名などの自己定義の主キー (teamPK 用) を使用してこれを行うことに注意してください。

于 2012-09-07T13:32:48.763 に答える