Batasan kunci asing pada anggota array?

27

Misalkan saya memiliki tabel yang berisi peran pekerjaan:

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

Misalkan saya lebih lanjut memiliki tabel, pengguna, dan setiap baris (pengguna tertentu) dapat memiliki jumlah peran pekerjaan yang berubah-ubah:

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

Saya mungkin harus memastikan bahwa setiap anggota users.roles[]ada di role.role. Tampaknya bagi saya bahwa apa yang saya inginkan adalah batasan kunci asing pada setiap anggota users.roles[]sedemikian rupa sehingga jika merujuk pada role.role.

Tampaknya ini tidak mungkin dengan postgres. Apakah saya melihat ini dengan cara yang salah? Apa cara "benar" yang disarankan untuk menangani ini?

pengguna2965107
sumber

Jawaban:

20

Dukungan untuk kunci asing array telah dikerjakan dengan tujuan memasukkannya ke PostgreSQL 9.3, tetapi itu tidak membuat potongan untuk rilis karena masalah kinerja dan keandalan. Tampaknya tidak sedang dikerjakan untuk 9,4.

Pada saat ini, Anda harus tetap menggunakan pendekatan relasional biasa menggunakan "join table" untuk memodelkan hubungan m: n.

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

Saya sarankan menggunakan kunci pengganti dalam kasus ini, juga, daripada menyimpan nama pengguna / nama peran langsung di tabel bergabung. Pertama kali Anda ingin mengganti nama pengguna atau peran, Anda akan senang Anda menggunakan kunci pengganti. Tempatkan uniquebatasan pada roles."role"dan users.username.

Craig Ringer
sumber
3

Saya baru saja membuat sesuatu yang serupa untuk seorang kolega. Pada dasarnya saya membuat tabel tersembunyi yang berisi satu baris untuk setiap pasangan (pengguna, peran) dengan batasan yang sesuai. Tabel pengguna kemudian merupakan tampilan tabel tersembunyi dengan semua peran yang dirangkai menjadi sebuah array. Saya kemudian memungkinkan untuk menyisipkan ke tampilan dengan menambahkan aturan yang sesuai. Begini caranya:

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=# 

Saya harap itu membantu. Anda dapat membuatnya sedikit lebih efisien dan menambahkan lebih banyak aturan tergantung pada kebutuhan Anda.

Max Murphy
sumber
1

Setelah Anda mendapatkan tambalan yang memungkinkan fungsi itu lebih banyak di sini

Cukup gunakan: ELEMENT REFERENCES relation( field )

Untuk niat:

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
);
Ian Mbae
sumber
1
Sepertinya ide yang bagus tetapi tidak dapat digunakan tanpa menambal mesin secara manual - ini adalah alasan untuk down-
vote