I have three database tables: ALIENS, MONSTERS, and TROPHIES.
Each ALIEN can have multiple TROPHIES.
Each MONSTER can have multiple TROPHIES.
Each TROPHY must have exactly one WINNER (ALIEN XOR MONSTER).
Is there a way to have a foreign key in the TROPHY table that references the primary key of either an ALIEN or a MONSTER?
Or is it easier to simply have two tables: an ALIEN_TROPHY table and a MONSTER_TROPHY table (even though they would be identical)?
You could create two foreign keys with a check constraint that says exactly one is empty:
create table alien (id int primary key);
create table monster (id int primary key);
create table trophy (id int primary key,
alien_id int references alien(id),
monster_id int references monster(id),
check (alien_id is null <> monster_id is null)
);
Related
I can not add a referential constraint, both tables has the two columns id and version as primary key. I will only check the integrity that the row is existing without checking version.
CREATE TABLE TABLEE
(ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
PRIMARY KEY (ID , VERSION);
CREATE TABLE CHAIR
(ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
ID_TABLEE INTEGER,
PRIMARY KEY (ID , VERSION);
ALTER Chair
ADD constraint MYC FOREIGN KEY (ID)
REFERENCES TABLEE (ID)
ON DELETE RESTRICT
Got this error
[Code: -573, SQL State: 42890] A column list specified in the references clause of constraint "MYC " does not identify a unique constraint of the parent table or nickname "TABLEE".. SQLCODE=-573, SQLSTATE=42890, DRIVER=4.28.11
The problem here is that Tablee its primary key consists of two columns id and version. But the integrity check should only be made on the referenced id, not version.
To establish a foreign key on a table the referenced column needs to:
Be a primary key on the other table.
...or at least act as one.
Since you already have a primary key on the referenced table, you can use the second approach and add a UNIQUE and NOT NULL constraints on ID.
For example:
CREATE TABLE TABLEE (
ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
PRIMARY KEY (ID , VERSION),
constraint uq1 unique (id) -- added UNIQUE constraint on ID
);
CREATE TABLE CHAIR (
ID INTEGER NOT NULL,
VERSION INTEGER NOT NULL,
ID_TABLEE INTEGER,
PRIMARY KEY (ID , VERSION)
);
ALTER table Chair
ADD constraint MYC FOREIGN KEY (ID)
REFERENCES TABLEE (ID)
ON DELETE RESTRICT;
See running example at db<>fiddle.
Note: If you want ID to have repeated values over the table, then ID is not a key, and cannot be referenced as one.
I have an entity table with multiple entity types and a table of entity relations which needs to enforce that it only contains relations between entities of the same type.
Currently, I have two approaches to this:
CREATE TABLE entity (
id uuid PRIMARY KEY,
type my_enum_type NOT NULL,
-- … more
);
CREATE TABLE relation (
id uuid PRIMARY KEY,
x uuid REFERENCES entity NOT NULL,
y uuid REFERENCES entity NOT NULL,
CHECK(entity(x).type = entity(y).type)
-- Doesn't work because CHECK cannot reference other tables.
);
Fortunately I currently have only two types, and don't expect that to change soon. But inheritance does not mix well with foreign keys, so it gets quite verbose:
CREATE TABLE entity (
id uuid PRIMARY KEY,
-- … more fields
);
CREATE TABLE entity_a (
PRIMARY KEY (id)
) INHERITS (entity);
CREATE TABLE entity_b (
PRIMARY KEY (id)
) INHERITS (entity);
CREATE TABLE relation (
id uuid PRIMARY KEY,
x uuid NOT NULL,
y uuid NOT NULL,
-- … more fields
);
CREATE TABLE relation_a (
PRIMARY KEY (id),
FOREIGN KEY (x) REFERENCES entity_a (id),
FOREIGN KEY (y) REFERENCES entity_a (id)
) INHERITS (relation);
CREATE TABLE relation_b (
PRIMARY KEY (id),
FOREIGN KEY (x) REFERENCES entity_b (id),
FOREIGN KEY (y) REFERENCES entity_b (id)
) INHERITS (relation);
The second approach definitely has the advantage that it works, but it's verbose, not extensible, and the only advantage over completely separate table definitions is that it avoids copying all additional fields (and then maybe forgetting to update them in both places).
Any suggestions on how to solve this more elegantly?
The following statements will add constraints to your solution 1) so that the condition is always satisfied:
/* we need a (redundant) UNIQUE constraint as target for foreign keys */
ALTER TABLE entity ADD UNIQUE (type, id);
/* add a (redundant) "type" column and fill it from "entity" */
ALTER TABLE relation ADD type my_enum_type;
UPDATE relation SET type = e.type
FROM entity AS e
WHERE relation.x = e.id;
ALTER TABLE relation ALTER type SET NOT NULL;
/* now we can add foreign keys that guarantee your condition */
ALTER TABLE relation ADD FOREIGN KEY (type, x) REFERENCES entity (type, id);
ALTER TABLE relation ADD FOREIGN KEY (type, y) REFERENCES entity (type, id);
/* remove the bloat (optional) */
VACUUM (FULL) relation;
True, it adds a redundant column and an redundant constraint, but I think it is the most elegant and natural way to ensure your condition.
I have three database tables: ALIENS, MONSTERS, and TROPHIES.
Each ALIEN can have multiple TROPHIES.
Each MONSTER can have multiple TROPHIES.
Each TROPHY must have exactly one WINNER (ALIEN XOR MONSTER).
Is there a way to have a foreign key in the TROPHY table that references the primary key of either an ALIEN or a MONSTER?
Or is it easier to simply have two tables: an ALIEN_TROPHY table and a MONSTER_TROPHY table (even though they would be identical)?
You could create two foreign keys with a check constraint that says exactly one is empty:
create table alien (id int primary key);
create table monster (id int primary key);
create table trophy (id int primary key,
alien_id int references alien(id),
monster_id int references monster(id),
check (alien_id is null <> monster_id is null)
);
I'm trying to create some Postgres tables, one has a foreign key on anothers' index:
CREATE TABLE Foo (ID SERIAL PRIMARY KEY);
CREATE TABLE File (FooId TEXT NOT NULL REFERENCES Foo (ID));
But it results in ERROR: foreign key constraint file_fooid_fkey cannot be implemented
Appears the types have to "match" or you'll get that error.
This works (change to int to match the size/type of SERIAL KEY which is an INT):
CREATE TABLE Foo (ID SERIAL PRIMARY KEY);
CREATE TABLE File (FooId INT NOT NULL REFERENCES Foo (ID));
This is a sample of my code. I'm using sql server 2008 R2
create table Entidade(
NIF numeric(9,0) primary key,
nome varchar(250) not null,
tipoEnt varchar(4) check (tipoEnt in ('CE','EC','CEEC',null))
)
create table Curso(
cod int primary key identity(1,1),
descricao text,
nHoras decimal(5,2),
NIFEnt numeric(9,0) references Entidade(NIF),
)
create table AccaoFormacao(
ref int identity(1,1) not null,
codCr int references Curso(cod) not null,
dtInicio date,
dtFim date,
BIFDR numeric(8,0) references Formador(BI),
constraint pkAccaoFormacao primary key(ref, codCr),
)
create table AF_FDO(
codCr int,
refAf int,
BI numeric(8,0) foreign key references Formando(BI),
constraint fkAF_FDO foreign key(codCr, refAf) references AccaoFormacao(codCr, ref),
constraint pkAF_FDO primary key(codCr, refAf, BI)
)
Everything goes fine until I try to create the table AF_FDO, it says:
"Msg 1776, Level 16, State 0, Line 1
There are no primary or candidate keys in the referenced table 'AccaoFormacao' that match the referencing column list in the foreign key 'fkAF_FDO'."
I understand the message but can't figure how to fix it since I'm declaring the primary key in the constraint pkAccaoFormacao it makes no sense to me.
The unique constraint in AccaoFormacao(codCr, ref) works but at the same time it doesn't "fit" in what I'm doing here. AccaoFormacao is a weak entity of Curso thus I have a composite primary key in AccaoFormacao.
EDIT1:
Ok I figured it out after hours I feels stupid.
I changed the constraint fkAF_FDO foreign key(codCr, refAf) references AccaoFormacao(codCr, ref) in AF_FDO to constraint fkAF_FDO foreign key(codCr, refAf) references AccaoFormacao and it worked. It will get the composite primary key of AccaoFormacao as it should, the other way should also work...
Thanks for the help anyway.
There is no PK in AccaoFormacao , take a look at Ref and CodCr , FKS needs to point a PK.