I am trying to design a relational database (postgres) which contains multiple tables under a single entity. In this example I have a store.
The store has many customers, staff, products. They are all related to the store through a "store_id"
I want to also link customers to a staff member. But I can't just add staff_id to the customers table, because this could allow a customer to be associated with a staff member from any store.
Is it possible to allow customers to be related only to staff which both the customer and staff are a member of?
Basically how do I prevent the below from happening?
Thanks for reading.
Solved this by applying a multi column foreign key constraint.
For customers, I keep the store_id foreign key, add a staff_id foreign key. Then also add a foreign key constraint against staff.store_id and staff.id
staff_id integer REFERENCES staff(id),
store_id integer REFERENCES stores(id),
CONSTRAINT customers_staff_id_store_id_fkey FOREIGN KEY (staff_id, store_id) REFERENCES staff(id, store_id)
This enforces a rule so customers can only be associated to staff of the same store
-- Person (user) USR exists.
--
user {USR}
PK {USR}
-- Store STO exists.
--
store {STO}
PK {STO}
-- Staff member (person) USR works at store STO.
--
staff {USR, STO}
PK {USR}
SK {USR, STO}
FK1 {USR} REFERENCES user {USR}
FK2 {STO} REFERENCES store {STO}
-- Customer (person) USR shops at store STO.
--
customer {USR, STO}
PK {USR}
SK {USR, STO}
FK1 {USR} REFERENCES user {USR}
FK2 {STO} REFERENCES store {STO}
-- Staff member STF manages account of customer CST
-- in store STO.
--
acc_mng {CST, STF, STO}
PK {CST}
FK1 {CST, STO} REFERENCES customer {USR, STO}
FK2 {STF, STO} REFERENCES staff {USR, STO}
Note:
All attributes (columns) NOT NULL
PK = Primary Key
AK = Alternate Key (Unique)
SK = Proper Superkey (Unique)
FK = Foreign Key
Related
I want to build the following defect detection system in SQL Server. The interface will be built using ASP.NET . but currently i am struggled on how to build the Database tables the relation between these tables.
the system allow to create a report >> select the report type,Equipment ID & other info >> select the wanted categories (by choosing Y or N) >> and for the selected category >> select the defect details and enter the comments..
I came out with this schema (Table names & Columns):-
Equipment
ID
Name
Operator
ID
Name
Report Type
ID
Name
Report
ID
Operator ID (FK to Operator),
Name
Equipment ID (FK to Equipment)
Type ID (FK to Report Type)
Date/Time
Comments
ReportCategory
ID
Name
Part
ID
Name
ReportCategory
Report ID (FK to Reports) -->PK
Category ID (FK to Category) --> PK
Yes/no
DefectDetailesLookup
Part ID (FK to Part) ---> PK
Cateogry ID (FK to Cateogry) ---> PK
ReportDefectDetails
ReportID (FK to Report) --> PK
Category ID (FK to Category) --> PK
Part ID (FK to Part)-->PK
Comment
Yes/No
so are the schema valid? or i am missing something? thanks
It would work.
There are a few things worth giving a thought for future changeability: Without fully understanding your applications domain: Give composite PKs a second thought. It is not necessarily a bad practise to use them, but if you use them make sure they are an integral part to the Entities Identity. And if you are not sure, I'd recommend to remove them and rather use an own Identity column for your Entity. Otherwise you are bound to having i.e. a Category for every ReportDefectDetail.
If you are not dependent on an existing database and assuming you already have your repository classes set up, give ef core and code-first a try. Also DDD (domain driven design) is a worthy read. Modern db models are mostly code driven/domain driven. The time of dbs dictating how the code should be written are gone.
Aside from that:
little typo here: DefectDetail_e_sLookup
ReportCategory => exists two times. I guess the upper one is just Category
give your yes/no columns meaningful names, like isDisplayed
-- Defect DEF named DEF_NME, with comment DEF_CMT exists.
--
defect {DEF, DEF_NME, DEF_CMT}
PK {DEF}
AK {DEF_NME}
-- Defect category CAT named CAT_NME exists.
--
dcat {CAT, CAT_NME}
PK {CAT}
AK {CAT_NME}
-- Defect DEF is in defect category CAT.
--
defect_category {DEF, CAT}
PK {DEF, CAT}
FK1 {DEF} REFERENCES defect {DEF}
FK2 {CAT} REFERENCES dcat {CAT}
-- Part PRT named PRT_NME exists.
--
part {PRT, PRT_NME}
PK {PRT}
AK {PRT_NME}
-- It is possible for part PRT to have defect DEF.
--
part_defect {PRT, DEF}
PK {PRT, DEF}
FK1 {PRT} REFERENCES part {PRT}
FK2 {DEF} REFERENCES defect {DEF}
-- Equipment EQP named EQP_NME exists.
--
equipment {EQP, EQP_NME}
PK {EQP}
AK {EQP_NME}
-- Equipment EQP contains part PRT.
--
equipment_part {EQP, PRT}
PK {EQP, PRT}
FK1 {EQP} REFERENCES equipment {EQP}
FK2 {PRT} REFERENCES part {PRT}
-- Operator 0PR named OPR_NME exists.
--
operator {0PR, OPR_NME}
PK {0PR}
AK {OPR_NME}
-- Report type RTY named RTP_NME exists.
--
report_type {RTY, RTP_NME}
PK {RTY}
AK {RTP_NME}
-- Report category RCT named RCT_NME exists.
--
report_cat {RCT, RCT_NME}
PK {RCT}
AK {RCT_NME}
-- Report REP, of report-type RTY, named REP_NME,
-- categorized in report-category RCT,
-- was submitted by operator OPR on date-time DTE,
-- for equipment EQP with comments REP_CMT.
report{REP, RTY, REP_NME, RCT, OPR, DTE, EQP, REP_CMT}
PK {REP}
AK {REP_NME}
SK {REP, EQP}
FK1 {RTY} REFERENCES report_type {RTY}
FK2 {OPR} REFERENCES operator {0PR}
FK3 {EQP} REFERENCES equipment {EQP}
FK4 {RCT} REFERENCES report_cat {RCT}
-- Defect DEF for part PRT of equipment EQP is reported in
-- report-detail number DET_NO of report REP; with
-- additional comments DET_CMT.
--
report_detail {REP, DET_NO, EQP, PRT, DEF, DET_CMT}
PK {REP, DET_NO}
FK1 {REP, EQP} REFERENCES report {REP, EQP}
FK2 {EQP, PRT} REFERENCES equipment_part {EQP, PRT}
FK3 {PRT, DEF} REFERENCES part_defect {PRT, DEF}
Note:
All attributes (columns) NOT NULL
PK = Primary Key
AK = Alternate Key (Unique)
SK = Proper Superkey (Unique)
FK = Foreign Key
I'm implementing a role base access control system, for which have the following database tables.
groups
---------
id (PK)
name
level
resources
---------
id (PK)
name
roles
---------
id (PK)
name
permissions
-----------
id (PK)
name
description
users
-----------
id (PK)
name
group_id(FK - references id on groups)
role_id(FK - references id on roles)
Groups has a many-to-many relationship with Resources and Roles. So I have the following junction tables.
group_resource
---------------
group_id(FK - references id on groups)
resource_id(FK - references id on resources)
group_role
---------------
group_id(FK - references id on groups)
role_id(FK - references id on roles)
Here is the issue:
Any given role within a group should have permissions for resources assigned to that group only.
I'm not entirely sure what would be the best way to model the relationship between roles, permissions, and resources in the context of group_resource and group_role relationships .
Any suggestions will be highly appreciated.
Thanks.
Here is a possible solution, with a certain degree of redundancy.
groups (id (PK), name, level)
roles (group_id (FK for groups) ,num_role, name) with PK (group_id, num_role)
users (id (PK), name, group_id, num_role) with (group_id, num_role) FK for roles
resource_types (id (PK), name)
group_resources (resource_type_id (FK for resource_types), group_id (FK for groups) with PK both the attributes
permissions (resource_type_id (FK for resource_types), group_id, num_role, description) with (group_id, num_role) FK for roles
With this solution, the application must check during the insertion of a permission, that the resource appear in the group_id specified in the permission, typically with a trigger.
A way of eliminating at all this redundancy (but it seems to me a less satisfying design), is to eliminate the relation group_resources, since all the information can be found through permissions.
-- Group GRP exists.
--
group {GRP}
PK {GRP}
-- Role ROL exists.
--
role {ROL}
PK {ROL}
-- Resource RES exists.
--
resource {RES}
PK {RES}
-- Role ROL exists within group GRP.
--
group_role {GRP, ROL}
PK {GRP, ROL}
FK1 {ROL} REFERENCES role {ROL}
FK2 {GRP} REFERENCES group {GRP}
-- Group GRP is assigned resource RES.
--
group_resource {GRP, RES}
PK {GRP, RES}
FK1 {GRP} REFERENCES group {GRP}
FK2 {RES} REFERENCES resource {RES}
-- Permission PER exists.
--
permission {PER}
PK {PER}
-- Permission PER is granted to role ROL
-- in group GRP for resource RES.
--
group_resource_permission {GRP, RES, ROL, PER}
PK {GRP, RES, ROL}
FK1 {GRP, RES} REFERENCES group_resource {GRP, RES}
FK2 {GRP, ROL} REFERENCES group_role {GRP, ROL}
FK3 {PER} REFERENCES permission {PER}
-- User USR is assigned role ROL in group GRP.
--
user {USR, GRP, ROL}
PK {USR}
FK1 {ROL} REFERENCES role {ROL}
FK2 {GRP} REFERENCES group {GRP}
-- User USR in role ROL of group GRP,
-- has permission PER to resource RES.
--
CREATE VIEW user_resource_permission
AS
SELECT u.USR
, x.RES
, x.PER
, u.GRP
, u.ROL
FROM user as u
JOIN group_resource_permission as x ON x.GRP = u.GRP
AND x.ROL = u.ROL ;
Note:
All attributes (columns) NOT NULL
PK = Primary Key
AK = Alternate Key (Unique)
SK = Proper Superkey (Unique)
FK = Foreign Key
We have two tables ManagementPlan which tells what type of product model has to be used.Based on this model number, a particular product of that type is assigned to a patient during therapy session.How can we model the many-many relation between Product table and ManagementPlan table
MangamentPlan(
PlanID(PK),
DiagnosisID(FK),
PhysicianID(FK),
PMCModelID,
Objective,
Description,
DateTime
)
Product(
PMCProductID(PK),
ManuProductID(FK),
ManufacturerID(FK),
PMCModelID,
Manufacturer model,
Features description,
PurchaseDate,
Storage Location
)
Add a junction table:
ManagementPlanProduct(PlanID(PK, FK(ManagementPlan)), PMCProductID(PK, FK(Product)))
You need a junction table
ManagementPlanProduct (
PlanID (PK, FK)
PMCProductID (PK, FK)
)
CREATE TABLE ManagementPlanProduct (
PlanID int NOT NULL,
PMCProductID int NOT NULL,
CONSTRAINT PK_mpp PRIMARY KEY CLUSTERED (PlanID, PMCProductID)
);
ALTER TABLE ManagementPlanProduct
ADD CONSTRAINT FK_mpp_mp
FOREIGN KEY (PlanID) REFERENCES ManagementPlan (PlanID)
ON DELETE CASCADE;
ALTER TABLE ManagementPlanProduct
ADD CONSTRAINT FK_mpp_p
FOREIGN KEY (PMCProductID) REFERENCES Product (PMCProductID)
ON DELETE CASCADE;
You can also add other columns to the junction table like quantity, date added, sort order and so on.
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.
I have 3 tables, like Employee, Department and Electronics tables.
Electronics table is child table for Department table and Department table is child table for Employee table.
I want to delete one record in Employee table where E_id=2 ( this is Primary key) this is Foreign key in Department table (E_id is Foreign key and Dept_id is Primary key) and Dept_id is Foreign Key in Electronics table.
First I want to delete related records in Electronics table then Department table and finally Employee table.
Please guide me how to do it.
You can read more about foreign key support in sqlite here: http://www.sqlite.org/foreignkeys.html
but you should be able to turn it on:
sqlite> PRAGMA foreign_keys = ON;
and then setup the database schema with the deletes cascading:
-- Database schema
CREATE TABLE Employee(
E_id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE Department(
Dept_id INTEGER PRIMARY KEY,
name TEXT,
E_id INTEGER REFERENCES Employee(E_id) ON DELETE CASCADE
);
CREATE TABLE Electronics(
Elec_id INTEGER PRIMARY KEY,
name TEXT,
Dept_id INTEGER REFERENCES Department(Dept_id) ON DELETE CASCADE
);
With all this in place and the data in the tables:
DELETE FROM Employee WHERE E_id = 2;