Foreign key referring to more than one primary key values(from one table) - Oracle SQL PLUS - sql

I am still a beginner in SQL and i'm facing an issue. hope you can help me.
I have a table called Department where it has an attribute DEPARTMENT_NO as its primary key.
CREATE TABLE DEPARTMENT(
DEPARTMENT_NO INT NOT NULL,
NAME VARCHAR(25) NOT NULL,
LOCATION CHAR(15),
PRIMARY KEY(DEPARTMENT_NO));
I have another table called Doctor where it has an attribute DNUM as a foreign key referring to DEPARTMENT_NO :
CREATE TABLE DOCTOR(
DOCTOR_ID CHAR(9) NOT NULL,
DNUM INT NOT NULL,
NAME VARCHAR(20) NOT NULL,
DOB DATE,
SPECIALTY VARCHAR(20) NOT NULL,
SALARY INT,
CITY VARCHAR(15),
STREET VARCHAR(15),
START_DATE DATE,
PRIMARY KEY(DOCTOR_ID))
FOREIGN KEY(DNUM) REFERENCES DEPARTMENT(DEPARTMENT_NO));
A doctor can be working in one or two departments. So, if I have a doctor working in department 1 and 4 (the values for DNUM will include 1 and 4).
I initially chose the data type of DNUM to be INT(same as DEPARTMENT_NO data type). But INT is not ideal for multiple values.
What should the data type be? or what other solution i have if,for example, I run a query for returning the name of the doctors working in department 4.
The query should return all the names of doctors working in department 4(only) and the ones who work in multiple departments(including 4).
Thanks very much in advance and sorry for the long message.

The standard way to represent a "many to many" relationship is via a "junction" (aka "link") table:
CREATE TABLE DOCTOR_DEPARTMENT (
DOCTOR_ID INT REFERENCES DOCTOR(DOCTOR_ID),
DEPARTMENT_NO INT REFERENCES DEPARTMENT (DEPARTMENT_NO),
PRIMARY KEY (DOCTOR_ID, DEPARTMENT_NO)
);
Note the key on {DOCTOR_ID, DEPARTMENT_NO}, which ensures the same doctor cannot be connected to the same department twice.
It also implicitly creates a composite (aka. "concatenated") index on these fields in that order, which makes it very quick to find departments of a given doctor (via an index range scan). If you need to query in the opposite "direction" (for doctors of the given department), flip the order of fields. If you need both queries, then you'll need both indexes (i.e. you'll need to create one index explicitly).
Consider adding ORGANIZATION INDEX clause, if you need just one of these indexes.

You need an additional table called doctor_department
create table doctor_department
(doctor_id integer references doctor(doctor_id) not null,
dnum integer references department(dnum) not null
)

You can create another table with relation to these 2 tables
Say,
Create table Dept_Doctors(
ID int not null,
DOCTOR_ID char(9) not null,
DEPARTMENT_NO INT NOT NULL,
PRIMARY KEY (ID),
FOREIGN KEY(DEPARTMENT_NO) REFERENCES DEPARTMENT(DEPARTMENT_NO),
FOREIGN KEY(DOCTOR_ID) REFERENCES DOCTOR(DOCTOR_ID));
You can join the 3 tables and get the desired result.

Related

There is no unique constraint matching given keys for referenced table "employee" 1

Here is my main table:
create table employee
(
eno SERIAL primary key,
ename varchar(100) not null,
title varchar(100) not null
);
I want to referenced the title only because I've already referenced the eno in another table.
create table pay
(
title varchar(100),
sal money not null,
foreign key(title) references employee(title)
);
I get an error that there is no unique constraint matching given keys for referenced table "employee" 1
Please help me. I'm having a hard time solving this error. I'm still a beginner at SQL. thanks a lot
There is no limit to the number of tables that may reference a given table.
Foreign keys may only reference primary keys.
Use a foreign key to eno:
create table pay
(
eno varchar(100),
sal money not null,
foreign key(eno) references employee(eno)
);

Cannot add foreign key constraint, composite PK and FK

CREATE TABLE HOSPITAL (
HOSP_CODE INT(3) NOT NULL,
HOSP_NAME VARCHAR(15),
HOSP_ADDRESS VARCHAR(15),
HOSP_PHONE VARCHAR(8),
HOSP_SUMBED INT(5),
PRIMARY KEY (HOSP_CODE)
);
CREATE TABLE WARD (
HOSP_CODE INT(3) NOT NULL,
WARD_CODE INT(3) NOT NULL,
WARD_NAME VARCHAR(20),
WARD_SUMBED INT(3),
PRIMARY KEY (HOSP_CODE, WARD_CODE),
FOREIGN KEY (HOSP_CODE) REFERENCES HOSPITAL(HOSP_CODE),
FOREIGN KEY (WARD_CODE) REFERENCES WARD(WARD_CODE)
);
I am trying to apply a constrain of a Primary Key that has 2 columns in it (HOSP_CODE and WARD_CODE) in table WARD and a Foreign Key constraint that is consisted of the two PK stated above.
Writing this code gives me the error stated in the title.
I have searched quite a lot about this error but couldn't find anything.
I understand that if there is a composite PK then the FK that refers to the same keys needs to be composite.
What I don't understand (and probably is the reason I have the error) is how can I assign the FK that refers to WARD_CODE.
Thank you in advance for your help and excuse me if I am unclear with my question.
P.S. I am new to SQL.
P.S2. The reason I need it this way is because of an assignment that a teacher gave us whose description mentions that table WARD should have a composite PK(HOSP_CODE,WARD_CODE) and two FKs for HOSP_CODE and WARD_CODE.
This is a bit long for a comment.
I think you might want three tables: Hospitals, Wards, and HospitalWards.
What you are calling Ward would be HospitalWards. You need a second table with one row per WardCode. I might think that the tables would look something like this:
CREATE TABLE WARDS (
WARD_CODE INT(3) NOT NULL,
WARD_NAME VARCHAR(20),
);
CREATE TABLE HospitalWards (
HOSP_CODE INT(3) NOT NULL,
WARD_CODE INT(3) NOT NULL,
WARD_NUMBED INT(3),
PRIMARY KEY (HOSP_CODE, WARD_CODE),
FOREIGN KEY (HOSP_CODE) REFERENCES HOSPITALS(HOSP_CODE),
FOREIGN KEY (WARD_CODE) REFERENCES WARDS(WARD_CODE)
);
The number of beds would be in the junction table. Many hospitals might have maternity wards, for instance, but each would have a different number of beds.
Note that I tend to name tables in the plural rather than the singular.

SQL constraint on a foreign key

I am having problems creating a constraint for a foreign key field, so that a foreign key can only be entered if that foreign key links to a row containing a specific attribute.
To give a better idea about what I mean, I have created the following example. The Employee entity table contains a Grade field, which can either be 'S' for a senior level employee, or 'J' for a junior level employee. In the Expenses table, I want to limit any entries into the ApprovedBy field to those EmpNo values that have a Grade field containing 'S' in the Employee table.
CREATE TABLE Employee
(EmpNo INT PRIMARY KEY,
FirstName VARCHAR(15),
LastName VARCHAR(15),
Grade CHAR(1),
CONSTRAINT chk_ValidGrade CHECK (Grade IN ('J','S'))
)
CREATE TABLE Expenses
(ExpenseId INT IDENTITY(1,1) PRIMARY KEY,
Amount FLOAT,
ApprovedBy INT FOREIGN KEY REFERENCES Employee(EmpNo),
)
I instinctively want to use a join, or other relational algebra function to do this. However, my understanding is that you can't use the SELECT function within a CHECK function, so I'm not sure how I would define the constraint.
As far as I know, you cannot use check constraint for this task. You have to use a trigger for insert and update in your Expenses table.

Foreign Key Problem in SQL

I just wanna ask coz I created a table with a column with two primary keys as shown below:
CREATE TABLE Parent(
Mother varchar(50) NOT NULL,
Father varchar(50) NOT NULL,
PRIMARY KEY(Mother, Father),
Organization varchar(50) NOT NULL
);
Then I am trying to create another table which will have a foreign key to the table shown above:
CREATE TABLE Child(
Name varchar(50) NOT NULL PRIMARY KEY,
Child_Mother varchar(50) REFERENCES Parent(Mother),
Child_Father varchar(50) REFERENCES Parent(Father),
Sibling varchar(50) NOT NULL
);
So I basically am trying to references two columns on the Child Table to a single column from the Parent table with two primary keys. Is this even possible? Thank you very much! :)
*Sample Tables only. But the actual are similar to this.
You haven't created a "table with a column with two primary keys", you've created one with a single composite primary key.
And what you are doing is indeed possible but not in the way you are doing it.
The problem is that you can have two Parent rows with the same Mother and different Father. But your foreign key relationship on Child_Mother will need a single instance of Mother in the Parent table otherwise it won't know which row it's referring to.
You can either have a combined column in the child which references the conbined primary key as you have it, or you can separate the Parent rows.
By that, I mean a parent table wouldn't normally put two people in a single row, it would normally have one row per person and the child would simply reference two separate rows in the Parent table, one for the mother and one for the father.
In terms of your actual tables:
create table Subject(
Subject_ID varchar(50) NOT NULL,
Subject_Description varchar(50) NOT NULL,
PRIMARY KEY(Subject_ID, Subject_Description),
Subject_SID int references Student(Student_ID),
Unit varchar(50) NOT NULL
)
This is not actually normalised since the subject description almost invariable depends on the subject ID. I think the primary key should probably just be on subject ID. By all means have another index on subject description but it shouldn't be part of the primary key.
I'm still not sure that you understand what I'm saying so I'll try to clarify. The line:
PRIMARY KEY(Subject_ID, Subject_Description)
does not make two primary keys, it makes a single primary key made up of the two columns (id and description). Because of that, it is possible to have two rows with the same ID provided that the description is different.
So a foreign key reference to the ID column is not possible. Let's say you had two rows:
ID Subject Other
-- ------- ---------
7 Maths blah blah
7 Physics yada yada
and then tried to add to the schedule table a row with Schedule_SID set to 7. That will choke because the DBMS wouldn't know which 7 you're referring to. So it shouldn't even let you set up that foreign key constraint because the target column isn't unique.
Try this one -
CREATE TABLE Child(
Name varchar(50) NOT NULL PRIMARY KEY,
Child_Mother varchar(50),
Child_Father varchar(50),
Sibling varchar(50) NOT NULL,
Foreign Key (Child_Mother, Child_Father) references Parent(Mother, Father)
);
Since you are creating a primary key which consists of two columns, you need to refer to them together.
What paxdiablo said is true. You are creating a composite primary key which consists of two columns. I would suggest you to use a Surrogate Primary Key.
Edit
CREATE TABLE Child(
id bigint NOT NULL auto_increment, // an "id" column
Name varchar(50) NOT NULL unique,
age int not null,
primary key (id) // "id" column is being declared as surrogate primary key
);
Second Edit
create table Subject(
id int not null,
Subject_ID varchar(50) NOT NULL,
Subject_Description varchar(50) NOT NULL,
PRIMARY KEY(id),
Subject_SID int references Student(Student_ID),
Unit varchar(50) NOT NULL
);
ALTER TABLE Subject ADD CONSTRAINT subject_unique UNIQUE (Subject_id, Subject_Description);
create table Schedule(
Day_MTWTHF varchar(50) NOT NULL,
TIME_HH varchar(50) NOT NULL,
subject_id int not null, -- my newly added column
Schedule_SID int references Student(Student_ID),
foreign key(subject_id) references Subject(id)
);
I'd say your choice of primary key is poor for the Parent table. It looks like you're using names. What happens if you've got two seperate couples whose names match up? John & Jane Smith with Alice and Bob for children, under your design, would also be the same John & Jane Smith from the next town over who've got Charlie and Dolores for kids, even though in reality they're completely unrelated.
Names are invariably a bad choice for keys, as names can be repeated. Using something guaranteed to be unique, such as an auto-incrementing integer, would be far safer, and would allow you to keep the J&J Smith from Springfield seperate from the J&J Smith from Shelbyville.

Enforce constraints between tables

How do you establish a constraint where, one column (not the primary key) has to have the same value as another table's column. I'm not quite sure how to phrase it so here's an example:
Ex:
I have three tables, Employee, Director, Division and Department
The structure for the tables are as follows:
Employee
Id
Name
DirectorId (FK)
DepartmentID (FK)
Director
Id
Name
DepartmentID (FK)
Department
Id
Name
DivisionId (FK)
Division
Id
Name
Employees and directors both have departments, each department has a division but their divisions have to be the same. Is there a way to enforce this? (Hopefully without having to resort to triggers)
Create stored procedures that will have proper GRANTS and don't allow user to INSERT into table directly. Use stored procedures as interface to the database, and check required conditions in them before insertion.
First, your example tables are doing too much. There is a design principle that states that a single table should model an entity or a relationship between entities but not both. The relationships between departments, directors and employees (I'm assuming that directors are not employees; I'm also omitting divisions for the moment).
Second, a table can have more than one key, known as candidate keys. Further, you can create a UNIQUE constraint by 'appending' a non-unique column to a key. For example, employees' names do not make for a good key, hence the reason for having an employee ID (I don't think the same can be said for departments i.e. department name in itself is a good enough key). If employee_ID is unique then it follows that (employee_name, employee_ID) will also be unique.
Third, a table can be referenced by any UNIQUE constraint, it doesn't have to be the table's 'primary' key (which partly explains why 'primary key' is a bit of a nonsense).
The great thing about the above is that one can model the required constraints using FOREIGN KEY and row-level CHECK constraints. SQL optimizers and programmers prefer declarative solutions to procedural code (triggers, stored procs, etc). This vanilla SQL DDL will port to most SQL products.
So, the department name can be combined with both the director key and the employee key respectively and these compound keys can be referencesd in a simple two-tier org chart table: because both the employee's department and their director's department will appear in the same table, a simple row-level CHECK constraint can be used to test that they are the same e.g.
Entity tables:
CREATE TABLE Departments
(
department_name VARCHAR(30) NOT NULL UNIQUE
);
CREATE TABLE Employees
(
employee_ID INTEGER NOT NULL UNIQUE,
employee_name VARCHAR(100) NOT NULL
);
CREATE TABLE Directors
(
director_ID INTEGER NOT NULL UNIQUE,
director_name VARCHAR(100) NOT NULL
);
Relationship tables:
CREATE TABLE EmployeeDepartments
(
employee_ID INTEGER NOT NULL UNIQUE
REFERENCES Employees (employee_ID),
employee_department_name VARCHAR(30) NOT NULL
REFERENCES Departments (department_name),
UNIQUE (employee_department_name, employee_ID)
);
CREATE TABLE DirectorDepartments
(
director_ID INTEGER NOT NULL UNIQUE
REFERENCES Directors (director_ID),
director_department_name VARCHAR(30) NOT NULL
REFERENCES Departments (department_name),
UNIQUE (director_department_name, director_ID)
);
CREATE TABLE OrgChart
(
employee_ID INTEGER NOT NULL UNIQUE,
employee_department_name VARCHAR(30) NOT NULL,
FOREIGN KEY (employee_department_name, employee_ID)
REFERENCES EmployeeDepartments
(employee_department_name, employee_ID),
director_ID INTEGER NOT NULL,
director_department_name VARCHAR(30) NOT NULL,
FOREIGN KEY (director_department_name, director_ID)
REFERENCES DirectorDepartments
(director_department_name, director_ID),
CHECK (employee_department_name = director_department_name)
);
Now a slightly more interesting scenario would be when a director is assigned a division, rather than a specific department, and you had to test that the employee's department was in the same division as her director:
Entity tables:
CREATE TABLE Divisions
(
division_name VARCHAR(20) NOT NULL UNIQUE
);
CREATE TABLE Departments
(
department_name VARCHAR(30) NOT NULL UNIQUE,
division_name VARCHAR(20) NOT NULL
REFERENCES Divisions (division_name),
UNIQUE (division_name, department_name)
);
CREATE TABLE Employees
(
employee_ID INTEGER NOT NULL UNIQUE,
employee_name VARCHAR(100) NOT NULL
);
CREATE TABLE Directors
(
director_ID INTEGER NOT NULL UNIQUE,
director_name VARCHAR(100) NOT NULL
);
Relationship tables:
CREATE TABLE EmployeeDepartments
(
employee_ID INTEGER NOT NULL UNIQUE
REFERENCES Employees (employee_ID),
employee_department_name VARCHAR(30) NOT NULL
REFERENCES Departments (department_name),
UNIQUE (employee_department_name, employee_ID)
);
CREATE TABLE DirectorDivisions
(
director_ID INTEGER NOT NULL UNIQUE
REFERENCES directors (director_ID),
director_division_name VARCHAR(20) NOT NULL
REFERENCES divisions (division_name),
UNIQUE (director_division_name, director_ID)
);
CREATE TABLE OrgChart
(
employee_ID INTEGER NOT NULL UNIQUE,
employee_department_name VARCHAR(30) NOT NULL,
FOREIGN KEY (employee_department_name, employee_ID)
REFERENCES EmployeeDepartments
(employee_department_name, employee_ID),
employee_division_name VARCHAR(20) NOT NULL
REFERENCES divisions (division_name),
FOREIGN KEY (employee_division_name, employee_department_name)
REFERENCES Departments (division_name, department_name),
director_ID INTEGER NOT NULL,
director_division_name VARCHAR(20) NOT NULL,
FOREIGN KEY (director_division_name, director_ID)
REFERENCES DirectorDivisions
(director_division_name, director_ID),
CHECK (employee_division_name = director_division_name)
);
There is no limitation on creating foreign keys--there's nothing to stop you from defining a foreign key constraint on these tables:
EMPLOYEE
DIRECTOR
...associating them to the DEPARTMENT table. Though frankly, I don't see the need for a DIRECTOR table--that should be either a boolean indicator in the EMPLOYEE table, or possibly an EMPLOYEE_TYPE_CODE with it's own foreign key constraint to distinguish between employees and directors.
Multiple Foreign Keys
The presence of a foreign key also doesn't stop you from putting a second (or third, etc) constraint on the same column. Consider the scenario of TABLE_C.column having foreign key constraints to both TABLE_A.col and TABLE_B.col -- this is perfectly acceptable in the database, but it means that only values that exist in both TABLE_A.col and TABLE_B.col can exist in the TABLE_C.column. IE:
TABLE_A
col
----
a
b
c
TABLE_B
col
----
c
Based on this example data, TABLE_C.column could only ever allow "c" as value to exist in the column if someone added two foreign key constraints to the TABLE_C.column, referencing TABLE_A.col and TABLE_B.col.