Restrição de chave estrangeira no membro da matriz?

27

Suponha que eu tenha uma tabela contendo funções de trabalho:

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

Suponha que eu ainda tenha uma tabela, usuários e cada linha (um usuário específico) possa ter um número arbitrário de funções:

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

Provavelmente eu deveria ter certeza de que cada membro users.roles[]existe em functions.role. Parece-me que o que eu quero é uma restrição de chave estrangeira em cada membro de users.roles[]tal forma que, se referir a role.role.

Isso não parece possível com o postgres. Estou vendo isso da maneira errada? Qual é a maneira "correta" sugerida para lidar com isso?

user2965107
fonte

Respostas:

20

O suporte para chaves estrangeiras de matriz foi trabalhado com o objetivo de inseri-lo no PostgreSQL 9.3, mas não foi suficiente para o lançamento devido a problemas de desempenho e confiabilidade. Não parece estar sendo trabalhado para a 9.4.

No momento, você precisa seguir a abordagem relacional usual de usar uma "tabela de junção" para modelar um relacionamento m: n.

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

Sugiro usar chaves substitutas também neste caso, em vez de armazenar os nomes de usuário / nomes de função diretamente na tabela de junção. Na primeira vez em que você deseja renomear um usuário ou função, ficará feliz por usar chaves substitutas. Basta colocar uma uniquerestrição em roles."role"e users.username.

Craig Ringer
fonte
3

Acabei de fazer algo semelhante para um colega. Essencialmente, criei uma tabela oculta que continha uma linha para cada par (usuário, função) com restrições adequadas. A tabela de usuários era então uma visão da tabela oculta com todas as funções reunidas em uma matriz. Tornei possível inserir na exibição adicionando uma regra apropriada. Aqui está como:

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

Espero que ajude. Você pode torná-lo um pouco mais eficiente e adicionar mais regras, dependendo de seus requisitos.

Max Murphy
fonte
1

Depois de obter o patch que permite essa funcionalidade mais aqui

Apenas use: ELEMENT REFERENCES relation( field )

Por intenção:

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
fonte
11
Parece que grande idéia, mas ele não pode ser usado sem aplicação de patches manualmente motor - esta é a razão para down-votos ...
langpavel