Mapping table between two sibling tables impossible in PostgreSQL? - sql

I am building a site with 2 templates: stats and tags but there is also an N:M mapping between them.
Stats and tags both have their own pages on the site and these have common characteristics, so I'd like a parent table called pages.
create table pages (id serial primary key, title varchar(50));
create table stats (show_average boolean) inherits (pages);
create table tags (color varchar(50)) inherits (pages);
create table stat_tags (
stat_id int
,tag_id int
,foreign key (stat_id) references stats(id)
,foreign key (tag_id) references tags(id)
);
This last query yields:
ERROR: there is no unique constraint matching given keys for referenced table "tags"
If I do it without inheritance like this:
create table stats (id serial primary key, title varchar(50), show_average boolean);
create table tags (id serial primary key, title varchar(50), color varchar(50));
create table stat_tags (
stat_id int
,tag_id int
,foreign key (stat_id) references stats(id)
,foreign key (tag_id) references tags(id)
);
... it accepts all queries.
Is it possible for two children to have a mapping table in PostgreSQL? How?
Thanks!

Using inherits probably isn't the best way to do this.
A serious limitation of the inheritance feature is that indexes
(including unique constraints) and foreign key constraints only apply
to single tables, not to their inheritance children. This is true on
both the referencing and referenced sides of a foreign key constraint.
See Caveats
I would be more comfortable with something along these lines.
create table pages (
id serial primary key,
page_type char(1) not null
check (page_type in ('s', 't')),
title varchar(50) not null unique,
unique (id, page_type)
);
create table stats (
id integer primary key,
page_type char(1) not null default 's'
check(page_type = 's'),
show_average boolean,
foreign key (id, page_type) references pages (id, page_type)
);
create table tags (
id integer primary key,
page_type char(1) not null default 't'
check(page_type = 't'),
color varchar(50) not null
);
create table stat_tags (
stat_id int not null,
tag_id int not null,
primary key (stat_id, tag_id),
foreign key (stat_id) references stats(id),
foreign key (tag_id) references tags(id)
);
In production, you'd probably want to build two updatable views, one to resolve the join between pages and stats, and one to resolve the join between pages and tags.

Related

Sql creating table consisting of keys from other tables

this is probably a simple question but I am quite new to SQL and databases, so I have been following this site: https://www.postgresqltutorial.com/postgresql-foreign-key/ to try and create a table that consist of primary keys from other tables.
Here I have the structure of the database in an excel overview. With colors showing the relations. i am having problems with the One-To-Many tables. As I get the same error every time "ERROR: column "id" referenced in foreign key constraint does not exist
SQL state: 42703".
The SQL query:
DROP TABLE IF EXISTS ingredient_to_unit_relations;
DROP TABLE IF EXISTS ingrediens;
CREATE TABLE ingrediens (
id serial,
name_of_ingredient varchar(255),
price_per_unit int,
PRIMARY KEY (id)
);
CREATE TABLE ingredient_to_unit_relations (
ingredient_relation_id int GENERATED ALWAYS AS IDENTITY,
PRIMARY KEY (ingredient_relation_id),
constraint Fk_ingredient_id
FOREIGN KEY (id)
REFERENCES ingrediens (id)
);
You need to define the column in order to declare it as a foreign key:
CREATE TABLE ingredient_to_unit_relations (
ingredient_relation_id int GENERATED ALWAYS AS IDENTITY,
ingredient_id int,
PRIMARY KEY (ingredient_relation_id),
constraint Fk_ingredient_id FOREIGN KEY (ingredient_id) REFERENCES ingrediens (id)
);
I might recommend some somewhat different naming conventions (I changed the name id in the table above):
CREATE TABLE ingredients (
ingredient_id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name varchar(255),
price_per_unit int
);
CREATE TABLE ingredient_to_unit_relations (
ingredient_relation_id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ingredient_id int,
CONSTRAINT Fk_ingredient_id FOREIGN KEY (ingredient_id) REFERENCES ingredients (ingredient_id)
);
Notes:
I am a fan of naming primary keys after the table they are in. That way, foreign keys and primary keys usually have the same name (and you can use using if you choose).
Avoid SERIAL. GENERATED ALWAYS AS IDENTITY is now recommended.
You can inline primary key constraints (as well as other constraints).
There is not generally a need to repeat the table name in a column (other than the primary key). So, name instead of name_of_ingredient.
Using int for a monetary column is suspicious. It doesn't allow smaller units. That might work for some currencies but in general I would expect a numeric/decimal type.

SQL: Is creating multiple foreign keys with one statement equivalent to creating them with one statement?

For example let's have the following table definition:
CREATE TABLE table1
(
id INT UNIQUE,
name VARCHAR(100) UNIQUE,
description VARCHAR(100),
PRIMARY KEY (id, name)
);
Now I would like to create another table which would have a foreign key to the above composite primary key. Would the following two statements be equivalent?
1)
CREATE TABLE table2
(
id INT PRIMARY KEY,
table1_id INT,
table1_name VARCHAR(100),
FOREIGN KEY (table1_id) REFERENCES table1(id),
FOREIGN KEY (table1_name) REFERENCES table1(name)
);
2)
CREATE TABLE table2
(
id INT PRIMARY KEY,
table1_id INT,
table1_name VARCHAR(100),
FOREIGN KEY (table1_id, table1_name) REFERENCES table1(id, name),
);
I noticed that behind the scenes Postgre SQL creates two FK db objects in the case of 1) and one FK object in the case of 2). Would everything work the same anyway?
Not at all. A foreign key reference should be to the primary key. In this case you have a composite primary key (although that is probably not needed, see below). Although foreign key references to unique keys are allowed (and some databases even allow foreign key references to any indexed columns), that is not a best practice.
When you use a composite primary key (your second example) you are guaranteeing that id/name are aligned in the first table. When you use separate references (your first example), you do not know that they are aligned, so the id could refer to one row in table1 and the name to another row. I doubt that is your intention.
In any case, repeating redundant data among tables is a bad practice. The better data model is:
CREATE TABLE table1 (
id INT PRIMARY KEY,
name VARCHAR(100) UNIQUE,
description VARCHAR(100),
);
CREATE TABLE table2 (
id INT PRIMARY KEY,
table1_id INT,
FOREIGN KEY (table1_id) REFERENCES table1(id)
);
Then, if you want the corresponding name, look up the name in the first table.
As a note, in Postgres, I would expect the INT to really be SERIAL so the database assigns a unique, increasing value when you insert new rows.
If you actually want two references to table1 then use two id references:
CREATE TABLE table2 (
id INT PRIMARY KEY,
table1_id INT,
table1_id_2 INT,
FOREIGN KEY (table1_id) REFERENCES table1(id),
FOREIGN KEY (table1_id_2) REFERENCES table1(id)
);

Insert null value in an association table

I have a problem with something in SQL, let's see an example of database :
CREATE TABLE person( //Employee
pe_id PRIMARY KEY NOT NULL AUTO_INCREMENT,
pe_name VARCHAR(20),
pe_office VARCHAR(20)
);
CREATE TABLE project( //Mission
pr_id PRIMARY KEY NOT NULL AUTO_INCREMENT,
pr_name VARCHAR(20),
pr_status VARCHAR(15)
);
CREATE TABLE techno( //Programming language
te_id PRIMARY KEY NOT NULL AUTO_INCREMENT,
te_name VARCHAR(20)
);
CREATE TABLE job( //developer, manager, ...
jo_id PRIMARY KEY NOT NULL AUTO_INCREMENT,
jo_name VARCHAR(20)
);
I would like to assign persons on projects for a job using technos.
For example, Rob works as a developer and project manager on the projet #13 with AngularJS and HTML.
So I created this table :
CREATE TABLE assignment(
pe_id INT,
pr_id INT,
te_id INT,
jo_id INT,
as_days INT, //Days of work
PRIMARY KEY(pe_id, pr_id, tr_id, jo_id),
CONSTRAINT fk_as_pe_id FOREIGN KEY(pe_id) REFERENCES person(pe_id),
CONSTRAINT fk_as_pr_id FOREIGN KEY(pr_id) REFERENCES project(pr_id),
CONSTRAINT fk_as_te_id FOREIGN KEY(te_id) REFERENCES techno(te_id),
CONSTRAINT fk_as_jo_id FOREIGN KEY(jo_id) REFERENCES job(jo_id)
);
I would like to have the ability to assign a developer with somes technos to a project without knowing who exaclty, like this:
INSERT INTO assignment(pr_id,te_id,jo_id,as_days) VALUES(1,2,3,4); //No person!
We suppose that this values exists in project, techno and job tables.
But it seems that I can not insert this, probably because I do not define person's ID (which is in the primary key).
How can I do this ?
Hope I'm understandable :)
You solve this problem by not having this as a primary key. Primary keys cannot be NULL or, if they're composite primary keys, cannot contain NULL. Make it a unique index instead. Create an autonumber field for the primary key. I think this is better solution in your case
Primary Key:
Can be only one in a table
It never allows null values
Primary Key is unique key identifier and can not be null and must be unique.
Unique Key:
Can be more than one unique key in one table.
Unique key can have null values(only single null is allowed).
It can be a candidate key
Unique key can be null and may not be unique.
Maybe you should do this:
Before insert disable constraint:
ALTER INDEX fk_as_pe_id ON assignment
DISABLE;
After insert enable it:
ALTER INDEX fk_as_pe_id ON assignment
REBUILD;
Another alternate way is, if it is possible to alter table structure, just exclude pe_id from the composite primary key in assignment table

SQL Multiple Foreign Keys as Primary Keys

If I declare the table below does it implicitly imply that both the foreign keys make a unique primary key or do I need to do something more to make both attributes as a primary key?
CREATE TABLE Report_has_Items
(
ReportID int REFERENCES Report(ReportID) NOT NULL,
ItemID int REFERENCES Item(ItemID) NOT NULL
)
Essentially both attributes which are foreign keys from other tables, together would form a unique key.
No it doesn't. The above table has no primary key. If you want to use the fields as a primary key use:
CREATE TABLE Report_has_Items(
ReportID int REFERENCES Report(ReportID) NOT NULL,
ItemID int REFERENCES Item(ItemID) NOT NULL,
PRIMARY KEY (ReportID, ItemID)
)
or something similar depending on your sql dilect.
Let's name our constraints, eh?
CREATE TABLE dbo.Report_has_Items(
ReportID int NOT NULL,
CONSTRAINT [FK_RHI_Report] (ReportId) REFERENCES dbo.Report(ReportID),
ItemID int NOT NULL,
Constraint [FK_RHI_Item] (ItemId) REFERENCES dbo.Item(ItemID),
CONSTRAINT [PK_RHI] PRIMARY KEY (ReportID, ItemID)
)
I am not sure i understand your question completely but i assume you are trying to create a composite primary key (primary key with more than one attribute). You could do the following.
CREATE TABLE Report_has_Items(
ReportID int references Report(ReportID),
ItemID int references Item(ItemID),
PRIMARY KEY (ReportID , ItemID )
);
Note:The pair (ReportID , ItemID ) must then be unique for the table and neither value can be NULL.
Here is a very useful link for SQL Queries

Problems understanding FOREIGN KEY / CASCADE constraints

I need some help at understanding how foreign keys and cascades work. I understood the theory but I'm having troubles to apply these to a real world example.
Let's assume I've got the following tables (and an arbitrary number of other tables that may reference table tags):
CREATE TABLE tags (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) UNIQUE
) Engine=InnoDB;
CREATE TABLE news (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(63),
content TEXT,
INDEX (title)
) Engine=InnoDB;
So I create a further table to provide the many-to-many relation between news and tags:
CREATE TABLE news_tags (
news_id INT UNSIGNED,
tags_id INT UNSIGNED,
FOREIGN KEY (news_id) REFERENCES news (id) ON DELETE ...,
FOREIGN KEY (tags_id) REFERENCES tags (id) ON DELETE ...
) Engine=InnoDB;
My requirements to the cascades:
If I delete a news, all corresponding entries in news_tags should be removed as well.
Same applies for table x that may be added later with x_tags-table.
If I delete a tag, all corresponding entries in news_tags and in every further table x_tags should be removed as well.
I'm afraid that I may have to revisit my table structure for this purpose, but that's alright since I'm only trying to figure out how stuff works.
Any links to good tutorials, SQL-queries or JPA-examples appreciated!
You seem to be proposing something like this, which sounds reasonable to me:
CREATE TABLE tags
(
id INTEGER NOT NULL,
name VARCHAR(20) NOT NULL,
UNIQUE (id),
UNIQUE (name)
);
CREATE TABLE news
(
id INTEGER NOT NULL,
title VARCHAR(30) NOT NULL,
content VARCHAR(200) NOT NULL,
UNIQUE (id)
);
CREATE TABLE news_tags
(
news_id INTEGER NOT NULL,
tags_id INTEGER NOT NULL,
UNIQUE (tags_id, news_id),
FOREIGN KEY (news_id)
REFERENCES news (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (tags_id)
REFERENCES tags (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);