Chicken or Egg problem in table schema [PostgreSQL] - database

How do I create and insert rows to the following table schema in PostgreSQL:
Table: employee
emp_id, emp_name, emp_dept, emp_manager
Table: department
dept_id, dept_name, dept_manager
emp_manager is a foreign key to employee(emp_id)
emp_dept is a foreign key to department(dept_id)
dept_manager is a foreign key to employee(emp_id)

It can work like this:
CREATE TABLE employee (
emp_id int PRIMARY KEY
, emp_dept int NOT NULL
, emp_manager int
, emp_name text NOT NULL
, CONSTRAINT fk_emp_manager FOREIGN KEY (emp_manager) REFERENCES employee(emp_id)
, UNIQUE (emp_dept, emp_id) -- needed for FK fk_dept_manager
);
CREATE TABLE department (
dept_id int PRIMARY KEY
, dept_manager int
, dept_name text NOT NULL
, CONSTRAINT fk_dept_manager FOREIGN KEY (dept_id, dept_manager) REFERENCES employee(emp_dept, emp_id)
);
ALTER TABLE employee
ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (emp_dept) REFERENCES department(dept_id);
Note how I change fk_dept_manager into a multicolumn FK reference to only allow employees of the same department to be department manager. Assuming you want that.
You might also want a CHECK constraint in table employee to disallow employees from being their own manager:
CHECK (emp_manager <> emp_id)
How to INSERT?
As usual. To overcome mutual dependencies, either make FK constraints DEFERRABLE and run multiple commands in a single transaction (more expensive) or use a single command with one or more CTEs.
Example: to insert a new department and a new employee as its manager at once:
WITH ins_dept AS (
INSERT INTO department
(dept_manager , dept_name)
VALUES (nextval(pg_get_serial_sequence('employee', 'emp_id')), 'Sales')
RETURNING *
)
INSERT INTO employee
(emp_id , emp_dept, emp_manager, emp_name)
SELECT dept_manager, dept_id , NULL, 'Bob'
FROM ins_dept;
db<>fiddle here
Further reading:
Complex foreign key constraint in SQLAlchemy
SET CONSTRAINTS ALL DEFERRED not working as expected
How to deal with mutually dependent inserts

Related

Is it better to have an Id as primary key or a double foreign key

I want to know what is the most optimized data structure:
I have 2 tables like:
CREATE TABLE Person
(
Id Int NOT NULL PRIMARY KEY,
name varchar(50)
)
CREATE TABLE Group
(
Id Int NOT NULL PRIMARY KEY,
nameOfGroup varchar(50)
)
In my table to connect persons to groups I want to know what is the most optimized way to create it between an ID and then the foreign keys, like that:
CREATE TABLE PersonGroup
(
Id Int DISTINCT NOT NULL PRIMARY KEY,
PersonId Int, -- which is a foreign key to table Person
GroupId Int -- which is a foreign key to table Group
)
OR having the 2 foreign keys as a primary because no one can be 2 times in the same group anyway, like that :
CREATE TABLE PersonGroup
(
PersonId Int NOT NULL, -- which is a foreign key to table Person
GroupId Int NOT NULL, -- which is a foreign key to table Group
CONSTRAINT PK_PersonSgroup PRIMARY KEY (PersonId, GroupId)
)
What is the most optimized between those 2 tables for querying after (if there is more optimize).
Thank you to have read my post.
The first solution (PK identity) is incomplete because you need to have an alternate (or surrogate) key (AK) compound of the two FK.
In the two possibilities, you must know that the order of the two FK in the PK or the AK is very important. This order must follow the usual querying way. So the questions are
do you use first the group then the person in queries ?
do you use first the personthen the group in queries ?
Also you need an index on the second PK, not the first of the PK or AK compound of the two PK.
For me the best table will be:
CREATE TABLE PersonGroup
(
PersonId Int NOT NULL REFERENCES Person (PersonId),
GroupId Int NOT NULL REFERENCES Group (GroupId),
CONSTRAINT PK_PersonSgroup PRIMARY KEY (PersonId, GroupId)
);
CREATE INDEX X_PersonGroup_FK_Group (GroupId);

Mass data loading on table where foreign key reference primary key in same table

I'm teaching myself SQL Server but have a background with "relational" databases for many years and understand keys and their linking relationships.
I have list of pre-defined INSERTS that I found somewhere and am trying and create a "employees" table. Within the table is a primary key on employee_id and with a foreign key on manager_id, references back to employee_id creating a hierarchy.
(
employee_id NUMERIC(3)
PRIMARY KEY,
first_name VARCHAR( 255 ) NOT NULL,
last_name VARCHAR( 255 ) NOT NULL,
email VARCHAR( 255 ) NOT NULL,
phone VARCHAR( 50 ) NOT NULL ,
hire_date DATE NOT NULL ,
manager_id NUMERIC(3) , -- fk
job_title VARCHAR( 255 ) NOT NULL,
CONSTRAINT fk_employees_manager
FOREIGN KEY( manager_id )
REFERENCES employees( employee_id )
ON DELETE NO ACTION
)
Here is snipet of INSERTS:
Insert into EMPLOYEES
(EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE,HIRE_DATE,MANAGER_ID,JOB_TITLE) values (107,'Summer','Payne','summer.payne#example.com','515.123.8181','07-JUN-16',106,'Public Accountant');
Insert into EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE,HIRE_DATE,MANAGER_ID,JOB_TITLE) values (106,'Rose','Stephens','rose.stephens#example.com','515.123.8080','07-JUN-16',2,'Accounting Manager');
Insert into EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE,HIRE_DATE,MANAGER_ID,JOB_TITLE) values (101,'Annabelle','Dunn','annabelle.dunn#example.com','515.123.4444','17-SEP-16',2,'Administration Assistant');
Insert into EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE,HIRE_DATE,MANAGER_ID,JOB_TITLE) values (1,'Tommy','Bailey','tommy.bailey#example.com','515.123.4567','17-JUN-16',null,'President');
Insert into EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE,HIRE_DATE,MANAGER_ID,JOB_TITLE) values (3,'Blake','Cooper','blake.cooper#example.com','515.123.4569','13-JAN-16',1,'Administration Vice President');
Insert into EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE,HIRE_DATE,MANAGER_ID,JOB_TITLE) values (2,'Jude','Rivera','jude.rivera#example.com','515.123.4568','21-SEP-16',1,'Administration Vice President');
Insert into EMPLOYEES (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE,HIRE_DATE,MANAGER_ID,JOB_TITLE) values (11,'Tyler','Ramirez','tyler.ramirez#example.com','515.124.4269','28-SEP-16',9,'Accountant');
Upon trying to INSERT the data, I get an error stating:
The INSERT statement conflicted with the FOREIGN KEY SAME TABLE constraint "fk_employees_manager". The conflict occurred in database "MyDB", table "dbo.employees", column 'employee_id'.
I understand I'm trying to add employee 107 with a manager id of 106 but as being the first INSERT, employee (or a manager) id 106 doesn't exist (yet).
So my quick solution was to create the employees table WITHOUT to foreign key, execute all of the inserts, then finally ALTER the employees table to add the foreign key on manager_id referencing to employee_id.
This worked with no errors.
But I'd like to know if there is a better way to do this. I'm semi aware of the ON CASCADE ON UPDATE, etc features on adding a foreign key, but not sure how/syntax to make this work when creating the table (before data loading).

Can we have two primary keys in one Foreign key field?

My db hierarchy is like Group -> Branch -> department.
Employee and Roles are different table ,tbl_Employee having FK groupid,branchid, departmentid and RoleId. If I assign an employee as a '1' which is a groupHead according to Roles table then I have to provide this employee's branchId and depId as well. But all Branches of the particular group comes under him . So I was thinking of writing all the branchIds which comes under him. How should I proceed ?
You can build a structure like this:
create table Groups (
GroupID char(2) not null primary key
)
create table Branches (
GroupID char(2) not null foreign key references Groups (GroupID),
BranchID char(2) not null primary key,
constraint UQ_Branch_XRef UNIQUE (GroupID,BranchID)
)
create table Departments (
GroupID char(2) not null foreign key references Groups (GroupID),
BranchID char(2) not null foreign key references Branches (BranchID),
DepartmentID char(2) not null primary key,
constraint UQ_Department_XRef UNIQUE (GroupID,BranchID,DepartmentID),
constraint FK_Department_Branch_XRef FOREIGN KEY (GroupID,BranchID)
references Branches (GroupID,BranchID)
)
create table Employees (
EmployeeID char(2) not null primary key,
GroupID char(2) not null references Groups (GroupID),
BranchID char(2) null references Branches (BranchID),
DepartmentID char(2) null references Departments (DepartmentID),
RoleID int not null,
constraint FK_Employee_Branch_XRef FOREIGN KEY (GroupID,BranchID)
references Branches (GroupID,BranchID),
constraint FK_Employee_Department_XRef FOREIGN KEY (GroupID,BranchID,
DepartmentID)
references Departments (GroupID,BranchID,DepartmentID),
constraint CK_AppropriateRole CHECK (
(RoleID = 1 and BranchID is null and DepartmentID is null) or
(RoleID = 2 and BranchID is not null and DepartmentID is null) or
(RoleID = 3 and BranchID is not null and DepartmentID is not null)
)
)
And then populate it like this:
insert into Groups (GroupID) values ('aa'),('bb')
insert into Branches (GroupID,BranchID) values
('aa','cc'),('aa','dd'),('bb','ee'),('bb','ff')
insert into Departments (GroupID,BranchID,DepartmentID) values
('aa','cc','gg'),
('aa','cc','hh'),
('aa','dd','ii'),
('bb','ee','jj'),
('bb','ff','kk'),
('bb','ff','ll')
And insert some valid employees
insert into Employees (EmployeeID,GroupID,BranchID,DepartmentID,RoleID) values
('mm','aa',null,null,1),
('nn','aa','dd',null,2),
('oo','bb','ee','jj',3)
But then you can't insert invalid rows:
insert into Employees (EmployeeID,GroupID,BranchID,DepartmentID,RoleID) values
('pp','bb',null,null,3)
insert into Employees (EmployeeID,GroupID,BranchID,DepartmentID,RoleID) values
('qq','aa','cc','hh',1)
insert into Employees (EmployeeID,GroupID,BranchID,DepartmentID,RoleID) values
('rr','aa','cc','ii',3)
And then it's implied that 'mm' is attached to group 'aa' and all branches and departments under it, 'nn' is attached to branch 'dd' of group 'aa' and all departments under that, and 'oo' is in department 'jj' of branch 'ee' of group 'bb'.
Whenever you store a NULL in (a column of) a foreign key, in SQL Server, then SQL Server doesn't enforce that foreign key.
The Constraints
There are 16 constraints in the 4 above tables, and most of them serve an important purpose. I'll ignore the primary keys since they're just fulfilling their usual role.
create table Branches (
GroupID char(2) not null foreign key references Groups (GroupID),
Just a plain foreign key. Nothing special yet
...
constraint UQ_Branch_XRef UNIQUE (GroupID,BranchID)
We need this unique constraint so that foreign keys can reference this table by both columns, so that we can cross-check that where a BranchID is used, it's used with the correct GroupID.
)
create table Departments (
GroupID char(2) not null foreign key references Groups (GroupID),
BranchID char(2) not null foreign key references Branches (BranchID),
The above two foreign key references aren't strictly necessary. The other FK defined below on this table will also ensure that the values are valid. But they are the "natural" FKs that most people would define. Whether you keep them or not is a matter of preference and possibly performance.
...
constraint UQ_Department_XRef UNIQUE (GroupID,BranchID,DepartmentID),
This fulfils a similar role to UQ_Branch_XRef above, allowing FKs to reference this table by all 3 columns to ensure that all of the values are consistent.
constraint FK_Department_Branch_XRef FOREIGN KEY (GroupID,BranchID)
references Branches (GroupID,BranchID)
And this is the first FK that makes use of UQ_Branch_XRef, ensuring that our GroupID column is appropriate for our chosen BranchID.
)
create table Employees (
...
GroupID char(2) not null references Groups (GroupID),
This FK constraint is important if both BranchID and DepartmentID are null - it's our only FK constraint that actually applies in such a circumstance.
BranchID char(2) null references Branches (BranchID),
DepartmentID char(2) null references Departments (DepartmentID),
These 2 FKs are technically redundant, given other FKs declared below, but they're the "natural" FKs.
...
constraint FK_Employee_Branch_XRef FOREIGN KEY (GroupID,BranchID)
references Branches (GroupID,BranchID),
This ensures that our GroupID and BranchID are correct. This FK is only important if DepartmentID is null, since in such a circumstance, the next FK won't apply a constraint
constraint FK_Employee_Department_XRef FOREIGN KEY (GroupID,BranchID,
DepartmentID)
references Departments (GroupID,BranchID,DepartmentID),
This is the constraint that ensures all 3 of the named columns match up with the entry in the Departments table.
constraint CK_AppropriateRole CHECK (
(RoleID = 1 and BranchID is null and DepartmentID is null) or
(RoleID = 2 and BranchID is not null and DepartmentID is null) or
(RoleID = 3 and BranchID is not null and DepartmentID is not null)
)
And finally this is the constraint that applies some business rules about what roles should have which columns filled in.
)

Can a foreign key of a table be a part of the composite primary key of the same table?

Suppose I have two tables: A (with columns: a,b,c,d) and B (with columns: x,y,z). Now, (a,b) together make the primary key for table A and x is the primary key of table B. Is it possible to make b a foreign key of table A that refers x from table B?
Please reply ASAP!
Thanks in advance! :-)
Yes, there is no issue with that. A classic example (using MySQL for demonstration purposes) is a database table holding a number of companies and another holding employees which can work for any one of those companies:
create table companies (
id int primary key,
name varchar(20));
create table employees (
id int,
c_id varchar(20) references companies(id),
name varchar(20),
primary key (id, c_id));
insert into companies (id, name) values (1, 'ABC');
insert into companies (id, name) values (2, 'DEF');
insert into companies (id, name) values (3, 'HIJ');
insert into employees (id, c_id, name) values (101, 1, "Allan");
insert into employees (id, c_id, name) values (102, 1, "Bobby");
insert into employees (id, c_id, name) values (101, 2, "Carol");
insert into employees (id, c_id, name) values (101, 3, "David");
Note that the primary key for employees is a composite key made up of the employee ID and company ID. Note also that the company ID is a foreign key constraint on the primary key of companies, the exact situation (functionally) that you asked about.
The query showing who works for what company shows this in action:
select c.id, c.name, e.id, e.name
from companies c, employees e
where c.id = e.c_id
order by c.id, e.id
c.id c.name e.id e.name
---- ------ ---- ------
1 ABC 101 Allan
1 ABC 102 Bobby
2 DEF 101 Carol
3 HIJ 101 David
Can a column in a composite primary key also be a foreign key referencing a primary key of another table? Of course it can. The important question is, when is this a good idea?
The most common scenario is probably the intersection or junction table. Customers can have more than one Address (Shipping, Billing, etc) and Addresses can have more than one Customer using them. So the table CUSTOMER_ADDRESSES has a primary key which references both CUSTOMER and ADDRESS primary key (and for bonus points the ADDRESS_TYPE reference data table too).
My examples use Oracle 12c syntax:
create table customer_address
( customer_id number(38,0) not null
, address_id number(38,0) not null
, address_type_code varchar2(3) not null
, constraint customer_address_pk primary key
(customer_id, address_id, address_type_code)
, constraint customer_address_customer_fk foreign key
(customer_id) references customer(customer_id)
, constraint customer_address_address_fk foreign key
(address_id) references address(address_id)
, constraint customer_address_type_fk foreign key
(address_type_code) references address_type(address_type_code)
);
The second scenario occurs when the primary key of the child table is comprises the parent key and an identifier (usually a number) which is only unique within the parent key. For instance, an Order has an Order Header and some Order Lines. The Order is identified by the Order Header ID and its lines are identified by a monotonically incrementing number. The ORDER_LINE table may look like this:
create table order_line
( order_header_id number(38,0) not null
, order_line_no number(38,0) not null
, product_id number(38,0) not null
, qty number(38,0) not null
, constraint order_line_pk primary key
(order_header_id, order_line_no)
, constraint order_line_header_fk foreign key
(order_header_id) references order_header(order_header_id)
, constraint order_line_product_fk foreign key
(product_id) references product(product_id)
);
Note that we could model ORDER_LINE as another intersection table, with a primary key of (order_header_id, product_id) and relegate order_line_no to the status of ordinary attribute: it depends on the business rules we must represent.
This second scenario is rarer than you might think: composite primary keys are pretty rare in real life. For instance, I think the model presented in that other answer is weak. The chances are we will need to use Employee as a foreign key for a number of relationships (e.g. Manager, Assignment, Sales). Using a composite key for foreign keys is clumsy (more typing!). Furthermore, when we drill into these models we often find that one of the key columns is a natural key rather than a primary key, and so might be subject to change. Cascading changes to natural key columns in composite foreign keys is a PITN.
Hence it is common practice to use a surrogate (or synthetic) primary key, say using a sequence or identify column, and enforce the natural key with a unique constraint. The latter step is often forgotten but it is crucial to maintaining referential integrity. Given a situation in which we need to store details of Employees from several Companies, including the Companies' Employee Identifier we might have an EMPLOYEE table like this:
create table employee
( employee_id number(38,0) generated always as number
, company_id number(38,0) not null
, company_employee_id varchar2(128) not null
, name varchar2(128) not null
, constraint employee_pk primary key
(employee_id)
, constraint employee_uk unique
(company_id, company_employee_id)
, constraint employee_company_fk foreign key
(company_id) references company(company_id)
);
One situation where it is common to find composite primary keys cascaded to dependent tables is in data warehouses and other VLDBs. Here the composite key columns form part of a denormalization strategy to support Partitioning schemes and/or efficient access paths.

multiple constraint keys

if i have tabled:
Resource (id (PK), name)
Manager (id(PK), resource_id (FK), manager_resource_ID(FK))
Should resource_id and manager_id both be foreign keys into the Resource table.
i obviously dont want to enter any values in each of those columns that are not proper resources
when i add the first relationship (resource_id <-> id) it works fine but
when i add the second one (manager_resource_id <-> id) it fails with the error:
Unable to create relationship [ . . .] The ALTER TABLE statement conflicted with the FOREIGN KEY constraint [... ]. The conflict occured in table Resource, column id
or do i need to break this out into 3 tables?
Resource(id, first, last)
Resource_manager(id, resource_id, manager_ID)
Manager(id)
Just a hint:
UPDATE:
If your model has employee-manager as many-to-many (bit unusual) then you could do:
CREATE TABLE Employee
(
EmployeeID int NOT NULL
,[Name] varchar(50)
)
go
ALTER TABLE Employee ADD
CONSTRAINT PK_Employee PRIMARY KEY CLUSTERED (EmployeeID ASC)
go
CREATE TABLE Manager
(
EmployeeID int NOT NULL
,ManagerID int NOT NULL
)
go
ALTER TABLE Manager ADD
CONSTRAINT PK_Manager PRIMARY KEY CLUSTERED (EmployeeID ASC, ManagerID ASC)
,CONSTRAINT FK1_Manager FOREIGN KEY (EmployeeID) REFERENCES Employee(EmployeeID)
,CONSTRAINT FK2_Manager FOREIGN KEY (ManagerID) REFERENCES Employee(EmployeeID)
,CONSTRAINT chk_Manager CHECK (EmployeeID <> ManagerID)
go
You have to create the foreign keys in the Manager table.

Resources