web-dev-qa-db-ja.com

配列メンバーの外部キー制約?

ジョブの役割を含むテーブルがあるとします。

CREATE TABLE roles
(
  "role" character varying(80) NOT NULL,
  CONSTRAINT "role" PRIMARY KEY (role)
);

さらにテーブルとユーザーがあり、各行(特定のユーザー)が任意の数のジョブロールを持つことができると仮定します。

CREATE TABLE users
(
  username character varying(12) NOT NULL,
  roles character varying(80)[] NOT NULL,
  CONSTRAINT username PRIMARY KEY (username)
);

おそらく、users.roles[]の各メンバーがroles.roleに存在することを確認する必要があります。必要なのは、users.roles[]の各メンバーに対する外部キー制約で、roles.roleを参照する場合のようです。

これはpostgresでは不可能のようです。私はこれを間違った方法で見ていますか?これを処理するための推奨される「正しい」方法は何ですか?

28
user2965107

配列の外部キーのサポートは、PostgreSQL 9.3に組み込むことを目的として行われましたが、パフォーマンスと信頼性の問題により、リリースの削減にはなりませんでした。それは9.4のために取り組んでいるようではありません。

現時点では、「結合テーブル」を使用してm:n関係をモデル化するという通常の関係アプローチに固執する必要があります。

CREATE TABLE user_roles (
   username character varying(12) references users(username),
   "role" character varying(80) references roles("role"),
   PRIMARY KEY(username, "role")
);

この場合も、ユーザー名/ロール名を直接結合テーブルに保存するのではなく、 代理キー を使用することをお勧めします。初めてユーザーまたはロールの名前を変更するときは、代理キーを使用して満足するでしょう。 unique制約をroles."role"users.usernameに配置するだけです。

20
Craig Ringer

私は同僚のために似たようなものを作りました。基本的に、適切な制約を使用して(ユーザー、ロール)のペアごとに1行を含む非表示のテーブルを作成しました。ユーザーテーブルは、すべてのロールが配列にまとめられた隠しテーブルのビューでした。次に、適切なルールを追加して、ビューに挿入できるようにしました。方法は次のとおりです。

trailer=# create table harvester (id int unique, label text);
CREATE TABLE
trailer=# insert into harvester values (1,'grain'), (2,'cricket');
INSERT 0 2
trailer=# create table donkey (id int, others int references
harvester(id));
CREATE TABLE
trailer=# create unique index donkey_ears on donkey (id, others);
CREATE INDEX
trailer=# create view combine as select id, array_agg(others) as others
from donkey group by id;
CREATE VIEW
trailer=# create rule combine_insert as on insert to combine do instead
(delete from donkey where donkey.id=new.id;insert into donkey select
new.id,unnest(new.others) );
CREATE RULE
trailer=# insert into combine values (1,'{1,2}');INSERT 0 2
trailer=# select * from combine ;
id | others 
----+--------
  1 | {1,2}
(1 row)

trailer=# insert into combine values (1,'{1,2}');
INSERT 0 2
trailer=# select * from combine ;
 id | others 
----+--------
  1 | {1,2}
    (1 row)

trailer=# insert into combine values (2,'{1,2,3}');
ERROR:  insert or update on table "donkey" violates foreign key
constraint "donkey_others_fkey"
DETAIL:  Key (others)=(3) is not present in table "harvester".
trailer=# 

お役に立てば幸いです。これをもう少し効率的にして、要件に応じてルールを追加できます。

3
Max Murphy

その機能を可能にするパッチを入手したら 詳細はこちら

ただ使用してください:ELEMENT REFERENCES relation( field )

一応:

CREATE TABLE drivers (
   driver_id integer PRIMARY KEY,
   first_name text,
   last_name text,
   ...
);



CREATE TABLE races (
   race_id integer PRIMARY KEY,
   title text,
   race_day DATE,
   ...
   practice1_positions integer[] ELEMENT REFERENCES drivers,
   practice2_positions integer[] ELEMENT REFERENCES drivers,
   practice3_positions integer[] ELEMENT REFERENCES drivers,
   qualifying_positions integer[] ELEMENT REFERENCES drivers,
   final_positions integer[] ELEMENT REFERENCES drivers
);
1
Ian Mbae