Обмеження зовнішнього ключа для члена масиву?


27

Припустимо, у мене є таблиця з робочими ролями:

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[]існує у role.role. Мені здається, що те, що я хочу, є обмеженням закордонного ключа для кожного члена users.roles[]такого, що, якщо посилається на role.role.

Це не видається можливим для постгресів. Я дивлюся на це неправильно? Який запропонований «правильний» спосіб вирішити це?

Відповіді:


20

Підтримка зовнішніх ключів масиву працювала з метою введення її в 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.


3

Я просто зробив щось подібне для колеги. По суті, я створив приховану таблицю, яка містила один рядок для кожної пари (користувача, ролі) з відповідними обмеженнями. Потім таблиця користувача являла собою перегляд прихованої таблиці з усіма ролями, зібраними в масив. Потім я дав можливість вставити у подання, додавши відповідне правило. Ось як:

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

Я сподіваюся, що це допомагає. Ви можете зробити його трохи ефективнішим і додати більше правил залежно від ваших вимог.


1

Як тільки ви отримаєте патч, що дозволяє більше функціонувати тут

Просто використовуйте: 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
Це виглядає як чудова ідея, але її неможливо використовувати без ручного виправлення двигуна - це причина для голосування вниз ...
langpavel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.