Fremdschlüsseleinschränkung für Arraymitglied?

27

Angenommen, ich habe eine Tabelle mit Jobrollen:

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

Angenommen, ich habe weiterhin eine Tabelle, Benutzer und jede Zeile (ein bestimmter Benutzer) kann eine beliebige Anzahl von Jobrollen haben:

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

Ich sollte wahrscheinlich sicherstellen, dass jedes Mitglied von users.roles[]rolls.role vorhanden ist. Es scheint mir, dass ich eine Fremdschlüsselbeschränkung für jedes Mitglied von users.roles[]so will, dass, wenn auf rolls.role verwiesen wird.

Dies scheint mit Postgres nicht möglich zu sein. Schaue ich das falsch an? Was ist der vorgeschlagene "richtige" Weg, um damit umzugehen?

user2965107
quelle

Antworten:

20

Die Unterstützung von Array-Fremdschlüsseln wurde mit dem Ziel bearbeitet, sie in PostgreSQL 9.3 zu integrieren. Aufgrund von Leistungs- und Zuverlässigkeitsproblemen wurde sie jedoch nicht für die Veröffentlichung freigegeben. Es scheint nicht für 9.4 gearbeitet zu werden.

Zu diesem Zeitpunkt müssen Sie sich an den üblichen relationalen Ansatz halten, eine "Verknüpfungstabelle" zum Modellieren einer m: n-Beziehung zu verwenden.

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

Ich empfehle auch in diesem Fall die Verwendung von Ersatzschlüsseln , anstatt die Benutzernamen / Rollennamen direkt in der Join-Tabelle zu speichern. Wenn Sie zum ersten Mal einen Benutzer oder eine Rolle umbenennen möchten, sind Sie froh, Ersatzschlüssel verwendet zu haben. Setzen Sie einfach eine uniqueEinschränkung auf roles."role"und users.username.

Craig Ringer
quelle
3

Ich habe gerade etwas Ähnliches für einen Kollegen gemacht. Im Wesentlichen habe ich eine versteckte Tabelle erstellt, die eine Zeile für jedes (Benutzer-, Rollen-) Paar mit geeigneten Einschränkungen enthält. Die Benutzertabelle war dann eine Ansicht der verborgenen Tabelle mit allen Rollen, die zu einem Array zusammengefasst wurden. Ich habe es dann möglich gemacht, in die Ansicht einzufügen, indem ich eine entsprechende Regel hinzugefügt habe. Hier ist, wie:

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

Ich hoffe das hilft. Sie können es ein bisschen effizienter gestalten und je nach Ihren Anforderungen weitere Regeln hinzufügen.

Max Murphy
quelle
1

Sobald Sie den Patch erhalten, der diese Funktionalität ermöglicht, erfahren Sie hier mehr

Benutz einfach: ELEMENT REFERENCES relation( field )

Zum Beispiel:

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
quelle
1
Es sieht nach einer großartigen Idee aus, kann aber nicht ohne manuelles Patchen der Engine verwendet werden - dies ist der Grund für Abstimmungen…
langpavel