parent and child table foreign key - postgresql

I currently have a parent table:
CREATE TABLE members (
member_id SERIAL NOT NULL, UNIQUE, PRIMARY KEY
first_name varchar(20)
last_name varchar(20)
address address (composite type)
contact_numbers varchar(11)[3]
date_joined date
type varchar(5)
);
and two related tables:
CREATE TABLE basic_member (
activities varchar[3]) // can only have 3 max activites
INHERITS (members)
);
CREATE TABLE full_member (
activities varchar[]) // can 0 to many activities
INHERITS (members)
);
I also have another table:
CREATE TABLE planner (
day varchar(9) FOREIGN KEY REFERENCES days(day)
time varchar(5) FOREIGN KEY REFERENCES times(time)
activity varchar(20) FOREIGN KEY REFERENCES activities(activity)
member bigint FOREIGN KEY REFERENCES members(member_id)
);
ALTER TABLE planner ADD CONSTRAINT pk_planner PRIMARKY KEY (day,time,activity,member);
I am currently trying to add with
INSERT INTO planner VALUES ('monday','09:00','Weights',2);
I have added a set into full_members with
INSERT INTO full_members
VALUES (Default, 'Hayley', 'Sargent', (12,'Forest Road','Mansfield','Nottinghamshire','NG219DX'),'{01623485764,07789485763,01645586754}',20120418,'Full');
My insert into Planner is currently not working — can you explain why?

i managed ot answer my own question it was becuase at the moment posgreSQL doesn't work very well with inheritence and foreign keys, so i have ot create a rule
CREATE RULE member_ref
AS ON INSERT TO planner
WHERE new.member NOT IN (SELECT member_id FROM members)
DO INSTEAD NOTHING;
this is basically the same as a foreign key

Not sure if this will be better solution but here it goes...
The principle is quite simple:
create new table lets call it table_with_pkeys which will replicate primary key column(s) of inherited tables child1, child2, child3...
create triggers on inherited tables, after insert, insert new PK into table_with_pkeys newly created PK, after update if it changes update it and after delete delete the same PK from table_with_pkeys.
Then in every table which should reference child1, child2 or whichever through parent table's PK using FK, point that FK not to parent's PK, but to table_with_pkeys which has copies of all childs PK's, and so you will have easy manageable way to have foreign keys that can cascade updates, restrict updates and so on.
Hope it helps.

You are missing an open quote before the 12 in the address:
INSERT INTO full_members
VALUES (Default, 'Hayley', 'Sargent', (12 Forest Road', 'Mansfield', 'Nottinghamshire', 'NG219DX'),
'{01623485764,07789485763,01645586754}',20120418,'Full');
should be:
INSERT INTO full_members
VALUES (Default, 'Hayley', 'Sargent', ('12 Forest Road', 'Mansfield', 'Nottinghamshire', 'NG219DX'),
'{01623485764,07789485763,01645586754}',20120418,'Full');

If the materialized view approach doesn't work for you above, create constraint triggers to check the referential integrity. Unfortunately declarative referential integrity doesn't work well with inheritance at present.

Related

Is there a way to create a unique constraint for 2 tables?

Currently, I have the following 3 tables.
CREATE TABLE customer (
id SERIAL PRIMARY KEY
);
CREATE TABLE google_subscription (
fk_customer_id INTEGER NOT NULL UNIQUE,
CONSTRAINT fk_customer_id_constraint
FOREIGN KEY(fk_customer_id)
REFERENCES customer(id)
ON DELETE RESTRICT
);
CREATE TABLE apple_subscription (
fk_customer_id INTEGER NOT NULL UNIQUE,
CONSTRAINT fk_customer_id_constraint
FOREIGN KEY(fk_customer_id)
REFERENCES customer(id)
ON DELETE RESTRICT
);
google_subscription is having fk_customer_id referencing to customer table id.
apple_subscription is having fk_customer_id referencing to customer table id.
I was wondering, is it ever possible to create a constraint, such that customer table id, will only be found in either google_subscription or apple_subscription, but NOT both?
No that is not possible. A constraint cannot span across multiple tables. Instead you can have another table subscriptions where you can have a unique index on customer(id). Or you can have another column in customer table which will hold only 1 subscription at a time.

Using tableoids as foreign keys in Postgresql

I was wondering whether there was any way possible to reference tableoid's as foreign keys in an inheritance relationship. For example:
CREATE TABLE employee
(
name TEXT,
PRIMARY KEY(name, TABLEOID)
);
CREATE TABLE hourly_employee
(
hours_worked INT,
PRIMARY KEY(name)
) INHERITS(employee);
CREATE TABLE salaried_employee
(
anniversary_date DATE,
PRIMARY KEY(name)
) INHERITS(employee);
CREATE TABLE employee_training
(
training_id SERIAL,
due_date DATE,
employee_name TEXT,
emp_oid OID,
PRIMARY KEY(training_id),
FOREIGN KEY(employee_name, emp_oid) REFERENCES employee(name, TABLEOID)
);
INSERT INTO hourly_employee (name, hours_worked) VALUES ('Joe Smith', 40);
INSERT INTO salaried_employee(name, anniversary_date) VALUES ('Bob Brown', '2014-02-20');
INSERT INTO employee_training (due_date, employee_name, emp_oid) VALUES ('2016-08-16', 'Bob Brown', 'salaried_employee'::REGCLASS);
In this example, the foreign key is created without a problem, but the last insert will fail with the error Key (employee_name, emp_oid)=(Bob Brown, 16403) is not present in table "employee" even though I can confirm that 16403 is the correct tableoid for salaried_employee.
Is there any way to make this work?
Sadly inheritance has some serious limitations. Several elements (including unique indexes / foreign keys) only apply to one table and not the children. Personally I've found it much less useful than I'd have liked it to be.
I know its annoying to suggest you re-design but in my opinion you'd be better to have a single table employee with optional columns instead of the parent / child relations.
CREATE TABLE employee
(
name TEXT,
employee_type TEXT,
hours_worked INT,
anniversary_date DATE,
PRIMARY KEY(name, TABLEOID)
);
In the long run you often find the code becomes simpler and frankly much more portable between DBMS as well.
You can ensure the correct fields have been entered for the correct type using constraints to manage which fields are mandatory for each type.
Eg:
ALTER TABLE employee ADD CHECK (
(type = 'hourly' and hours worked is not null)
or (type = 'salaried' and anniversary_date is not null))

Why does this foreign key using inheritance not work? [duplicate]

This question already has answers here:
PostgreSQL foreign key not existing, issue of inheritance?
(2 answers)
Closed 8 years ago.
create table abstract_addresses (
address_id int primary key
);
create table phone_numbers (
phone_number text not null unique
) inherits (abstract_addresses) ;
create table contacts (
name text primary key,
address_id int not null references abstract_addresses(address_id)
);
insert into phone_numbers values (1, '18005551212'); --works
select * from abstract_addresses;
address_id
1
select * from phone_numbers;
address_id phone_number
1 18005551212
insert into contacts values ('Neil', 1); --error
I get this error message:
ERROR: insert or update on table "contacts" violates foreign key constraint "contacts_address_id_fkey"
SQL state: 23503
Detail: Key (address_id)=(1) is not present in table "abstract_addresses".
Just a bad use-case for postgresql table inheritance?
Per the caveats in the docs:
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.
http://www.postgresql.org/docs/current/static/ddl-inherit.html
To do what you want:
Create a table with only an id — like you did.
Don't use inheritance. Really don't. It's useful to partition log tables; not for what you're doing.
Make phone number ids default to nextval('abstract_addresses_address_id_seq'), or whatever the sequence name is.
Add a foreign key in phone_numbers referencing abstract_addresses (address_id). Make it deferrable, initially deferred.
Add an after insert trigger on phone_numbers that inserts a new row in abstract_addresses when needed.
If appropriate, add an after delete trigger on phone_numbers that cascade deletes abstract_addresses — make sure it occurs after the delete, else affected rows will report incorrect values when you delete from phone_numbers.
That way, you'll have an abstract_address for use in occasional tables that need such a thing, while still being able to have a hard reference to phone_numbers where the latter is what you actually want.
One caveat to be aware of: it doesn't play well with ORMs.

Foreign Key referencing inherited table

I have the following tables:
CREATE TABLE mail (
id serial,
parent_mail_id integer,
...
PRIMARY KEY (id),
FOREIGN KEY (parent_mail_id) REFERENCES mail(id),
...
);
CREATE TABLE incoming (
from_contact_id integer NOT NULL REFERENCES contact(id),
...
PRIMARY KEY (id),
---> FOREIGN KEY (parent_mail_id) REFERENCES mail(id), <---
...
) INHERITS(mail);
CREATE TABLE outgoing (
from_user_id integer NOT NULL REFERENCES "user"(id),
...
PRIMARY KEY (id),
--> FOREIGN KEY (parent_mail_id) REFERENCES mail(id), <--
...
) INHERITS(mail);
incoming and outgoing inherit from mail and define their foreign keys (and primary keys) again, as they are not automatically inherited.
The problem is:
If I'd insert an incoming mail, it is not possible to reference it from the outgoing table as the foreign key only works with the super table (mails).
Is there a workaround for that?
PostgreSQL 9.3 docs:
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.
Thus, in the terms of the above example:
If we declared cities.name to be UNIQUE or a PRIMARY KEY, this would not stop the capitals table from having rows with names
duplicating rows in cities. And those duplicate rows would by default
show up in queries from cities. In fact, by default capitals would
have no unique constraint at all, and so could contain multiple rows
with the same name. You could add a unique constraint to capitals, but
this would not prevent duplication compared to cities.
Similarly, if we were to specify that cities.name REFERENCES some other table, this constraint would not automatically propagate to
capitals. In this case you could work around it by manually adding the
same REFERENCES constraint to capitals.
Specifying that another table's column REFERENCES cities(name) would allow the other table to contain city names, but not capital
names. There is no good workaround for this case.
These deficiencies will probably be fixed in some future release, but
in the meantime considerable care is needed in deciding whether
inheritance is useful for your application.
And not really a workaround, so maybe make mails a non-inherited table, and then separate incoming_columns and outgoing_columns for their respective extra columns, with the mail id as both their primary and foreign key. You can then create a view outgoing as mail INNER JOIN outgoing_columns, for example.
You may use a constraint trigger
CREATE OR REPLACE FUNCTION mail_ref_trigger()
RETURNS trigger AS
$BODY$
DECLARE
BEGIN
IF NOT EXISTS (
SELECT 1 FROM mail WHERE id = NEW.parent_mail_id
) THEN
RAISE foreign_key_violation USING MESSAGE = FORMAT('Referenced mail id not found, mail_id:%s', NEW.parent_mail_id);
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
CREATE CONSTRAINT TRIGGER mail_fkey_trigger
AFTER UPDATE OR INSERT ON incoming
DEFERRABLE
FOR EACH ROW EXECUTE PROCEDURE mail_ref_trigger();

How to make a Foreigne key that references NOT Primary or Unique column from another table?

I have made classical Employee - Employer table that has one primary key and one foreign key. Foreign key references primary so it is a self referencing table:
CREATE TABLE Worker
(
OIB NUMERIC(2,0),
Name NVARCHAR(10),
Surname NVARCHAR(20),
DateOfEmployment DATETIME2 NOT NULL,
Adress NVARCHAR(20),
City NVARCHAR(10),
SUPERIOR NUMERIC(2,0) UNIQUE,
Constraint PK_Worker PRIMARY KEY(OIB),
CONSTRAINT FK_Worker FOREIGN KEY (Superior) REFERENCES Worker(OIB)
);
Second table that should keep points for all my employees is made like this:
CREATE TABLE Point
(
OIB_Worker NUMERIC(2,0) NOT NULL,
OIB_Superior NUMERIC(2,0) NOT NULL,
Pt_To_Worker tinyint,
Pt_To_Superior tinyint,
Month_ INT NOT NULL,
Year_ INT NOT NULL,
CONSTRAINT FK_Point_Worker FOREIGN KEY (OIB_Worker) References Worker(OIB),
CONSTRAINT FK_Point_Worker_2 FOREIGN KEY (OIB_Superior) References Worker(Superior),
CONSTRAINT PK_Point PRIMARY KEY(OIB_Worker,OIB_Superior,Month_,Year_)
);
It should enable storing grades per month for every employee.
That is, I have two foreign keys, one for Worker.OIB and one for Worker.Superior. Also, I have a composite primary key made of columns Point.OIB_Worker, Point.OIB_superior, Point.Month_ and Point.Year_. Key is composite because it needs to disable entering grades more then once a month.
My question is:
How to make a foreign key from Point to Worker so that any superior can have more then one employee assigned to him?
If you look closely, my implementation works but it can have only one employee per manager.
That is because of a fact that a foreign key has to reference either the primary or the unique column from other table. And my Worker.Superior is UNIQUE, so it can have only unique values (no repetition).
I think many people will find this example interesting as it is a common problem when making a new database.
I think your FK_Point_Worker_2 should also have References Worker(OIB), and you should remove the UNIQUE constraint from Worker.Superior. That way a superior can have more than one worker assigned to him.
Think about it. You have unique constraint on SUPERIOR and you are confused as to why two employees cannot have the same SUPERIOR. That is what a unique constraint does - not allow duplicates.
A FK can only reference a unique column or columns.
A FK_Point_Worker_2 with a References Worker(OIB) does not assure OIB is a Superior.
I would add a unique constraint on Worker on (OIB, SUPERIOR)
and remove the unique constraint on SUPERIOR.
It will always be unique as OIB is unique.
Then have composite FK relationship.
This is an example of a composite FK relationship
ALTER TABLE [dbo].[wfBchFolder] WITH CHECK ADD CONSTRAINT [FK_wfBchFolder_wfBch] FOREIGN KEY([wfID], [bchID])
REFERENCES [dbo].[WFbch] ([wfID], [ID])
GO