Normalization the table to 3NF - database

I have trouble in understanding how do I represent the foreign key after I normalize the table to 3NF.
Lets consider the table this way:
student(studentid,studentname,courseid,coursename,dateofexam,marks,grade)
I can able to figure out the FD as follows:
studentid -> studentname
courseid -> coursename
{student,courseid} -> dateofexam,marks
marks -> grade
Now I'm trying to create a new table for FD that has non-ket attributes like this:
marks grade
studentid studentname
Now how do I relate the foreign key for these tables with my old table student? I'm confused on doing this.
Thanks in advance.

Your FD actually shows you how:
studentid -> studentname
courseid -> coursename
{student,courseid} -> dateofexam,marks
marks -> grade
Each row would be a separate table.
The keys that exists in other tables would be the fk.
In this case:
{student,courseid} -> dateofexam,marks
that would be studentid, courseid and marks(which probably must be an id too)
It would be something like this
Student(
studentid,
studentname)
Course(
courseid,
coursename)
Exams(
examid,
fk_studentid,
fk_courseid,
dateofexam,
fk_markid)
Marks(
markid,
grades)

Related

How do we give constraint on foreign key column based on ERD cardinality?

Let's say I have a 2 tables like below :
Department:
DeptId Name
Employee:
EmpId Name DeptId
Here I have a 0-to-many relationship between Department and Employee table.
Now when I read the relationship from Department to Employee, it says 1 department can have 0 or more employees.
Does this mean while creating Employee table, I will set DepartmentId (foreign key) as null due to zero or many relationship?
And how do we represent 1 or many relationship like for eg If relationship in ERD says 1 department can have 0 or more employees then DepartmentId (foreign key) will be non nullable in Employee table?
Basically I am just trying to understand how do we give constraint on foreign key based on ERD cardinality (1 to many relation,1 to zero or more relationship).
Can anyone please help me clear this confusion? I will really really appreciate that :)
I will set DepartmentId(Foreign key) as null due to zero or many
relationship?
No, the Foreign key should be not null. Imagine you have no employee in table Employee with DeptId = 1. So it represents the Department has zero relationships.
Otherwise, the Employee table contains multiple items with DeptId = 2, So it represents the 1-n relationships between Department and Employee table.
ERD tools are great, but the most powerful design tool is still natural language, predicates, and constraints. So, when in doubt, use a plain text editor instead of an ERD tool. Also do not allow NULLs in the design phase.
All attributes (columns) NOT NULL
[p x] = predicate x
(c x.y) = constraint x.y
PK = Primary Key
AK = Alternate Key (Unique)
FK = Foreign Key
Starting with the department, this part does not change.
[p 1] Department identified by number DEPT_ID, named DEPT_NAME exists.
(c 1.1) Department is identified by department number.
(c 1.2) For each department name, exactly one department has that name; for each department, that department has exactly one name.
department {DEPT_ID, DEPT_NAME} -- p 1
PK {DEPT_ID} -- c 1.1
AK {DEPT_NAME} -- c 1.2
Case 1: Emp * ---- 1 Dept
[p 2] Employee identified by number EMP_ID, named EMP_NAME, works for department DEPT_ID.
(c 2.1) Employee is identified by employee number.
(c 2.2) Each employee has exactly one name; for each employee name, more than one employee can have that name.
(c 2.3) Each employee works for exactly one department; for each department more than one employee may work for that department.
(c 2.4) If an employee works for a department, then that department must exist.
employee {EMP_ID, EMP_NAME, DEPT_ID} -- p 2
PK {EMP_ID} -- c 2.1, c 2.2, c 2.3
FK {DEPT_ID} REFERENCES department {DEPT_ID} -- c 2.4
Case 2: Emp * ---- 0..1 Dept
[p 2] Employee identified by number EMP_ID, named EMP_NAME, exists.
(c 2.1) Employee is identified by employee number.
(c 2.2) Each employee has exactly one name; for each employee name, more than one employee can have that name.
[p 3] Employee identified by number EMP_ID works for department identified by number DEPT_ID.
(c 3.1) Each employee works for at most one department; for each department more than one employee may work in that department.
(c 3.2) If an employee works for a department, then that employee must exist.
(c 3.3) If an employee works for a department, then that department must exist.
employee {EMP_ID, EMP_NAME} -- p 2
PK {EMP_ID} -- c 2.1, c 2.2
emp_dept {EMP_ID, DEPT_ID} -- p 3
PK {EMP_ID} -- c 3.1
FK1 {EMP_ID} REFERENCES employee {EMP_ID} -- c 3.2
FK2 {DEPT_ID} REFERENCES department {DEPT_ID} -- c 3.3
Case 3: Emp * ---- * Dept
[p 2] Employee identified by number EMP_ID, named EMP_NAME, exists.
(c 2.1) Employee is identified by employee number.
(c 2.2) Each employee has exactly one name; for each employee name, more than one employee can have that name.
[p 3] Employee identified by number EMP_ID works for department identified by number DEPT_ID.
(c 3.1) Each employee may work for more than one department; for each department more than one employee may work in that department.
(c 3.2) If an employee works for a department, then that employee must exist.
(c 3.3) If an employee works for a department, then that department must exist.
employee {EMP_ID, EMP_NAME} -- p 2
PK {EMP_ID} -- c 2.1, c 2.2
emp_dept {EMP_ID, DEPT_ID} -- p 3
PK {EMP_ID, DEPT_ID} -- c 3.1
FK1 {EMP_ID} REFERENCES employee {EMP_ID} -- c 3.2
FK2 {DEPT_ID} REFERENCES department {DEPT_ID} -- c 3.3
Case 4: Emp * ---- 1..* Dept
This one is tricky, combine cases 1 and 3 by introducing the concept of a home (primary, default) department.
[p 2] Employee identified by number EMP_ID, named EMP_NAME, works for home department HOME_DEPT_ID.
(c 2.1) Employee is identified by employee number.
(c 2.2) Each employee has exactly one name; for each employee name, more than one employee can have that name.
(c 2.3) Each employee works for exactly one home department; for each home department more than one employee may work for that home department.
(c 2.4) If an employee works for a home department, then that department must exist.
[p 3] In addition to the home department, employee identified by number EMP_ID also works for another department identified by number DEPT_ID.
(c 3.1) In addition to the home department, each employee may work for more than one other department; for each department more than one employee may work for that department.
(c 3.2) If an employee works for a department, then that employee must exist.
(c 3.3) If an employee works for a department, then that department must exist.
employee {EMP_ID, EMP_NAME, HOME_DEPT_ID} -- p 2
PK {EMP_ID} -- c 2.1, c 2.2, c 2.3
FK {HOME_DEPT_ID} REFERENCES department {DEPT_ID} -- c 2.4
emp_dept {EMP_ID, DEPT_ID} -- p 3
PK {EMP_ID, DEPT_ID} -- c 3.1
FK1 {EMP_ID} REFERENCES employee {EMP_ID} -- c 3.2
FK2 {DEPT_ID} REFERENCES department {DEPT_ID} -- c 3.3
On the application level check that, for a given employee, HOME_DEPT_ID is not repeated as EMP_ID in the emp_dept.
It wouldn't be null because every employee has a department. A department can have zero employees though, meaning its ID is in none of the employee rows.

How create procedure to retrieve data from three tables with user inserted data

I have three tables DEPARTMENTS, SEMESTER and SUBJECTS
CREATE TABLE DEPARTMENTS
(
D_ID INT PRIMARY KEY identity(1, 1),
department_name VARCHAR(50) NOT NULL
);
CREATE TABLE SEMESTER
(
D_ID INT FOREIGN KEY REFERENCES departments(D_ID),
sem_id INT PRIMARY KEY identity(1, 1),
semester INT CHECK
(semester BETWEEN 1 AND 8) NOT NULL
);
CREATE TABLE SUBJECTS
(
D_ID INT FOREIGN KEY REFERENCES departments(D_ID),
sem_id INT FOREIGN KEY REFERENCES semester(sem_id),
sub_id INT PRIMARY KEY identity(1, 1),
sub_name VARCHAR(50),
syllabus VARBINARY(MAX),
exam_format VARBINARY(MAX)
);
If user insert department_name in DEPARTMENTS table and semester from semester table then how to get sub_name from subject
Firstly, your design is wrong. you don't need to have a reference from Subject to Departments because Department is accessible through Semester and the D_Id in Subject is redundant, IMHO.
Second, what is your criteria for retrieving subject?
Select Sub_Name
from Subjects as sbj
inner join Departments as dpt on sbj.D_Id = dpt.D_Id
inner join Semester as smt on sbj.sem_id = smt.sem_id
where (YOUR CRITERIA)
This is basically the general correlation among your tables,
so you could specify the conditions and list of fields to be retrieved.
However it's best to rectify your design first and then access department
through Semester :
inner join Departments as dpt on smt.D_Id = dpt.D_Id
thereby you're gonna use the dependency in semester rather than subjects!
p.s.
if the department in subject is different from the on in semester
then your layout is good as it is, but if they're same thing then
you can get rid of the latter
You need to use join here
select sub.sub_name,dep.department_name,sem.semester
from subjects sub
join departments dep on dep.d_id = sub.d_id
join semester sem on sem.sem_id = sub.sem_id;

Using the count function in oracle to display a student who hasnt registered for a course

I have to list the student name and the number of courses he or she has taken (Include students who have not yet taken any class)
Here are 3 of my 5 tables, shouldn't need faculty and course table
--Student Table
CREATE TABLE Student(
Std_ID NUMBER(4) CONSTRAINT Student_ID_pk PRIMARY KEY,
Std_FN VARCHAR2(20),
Std_LN VARCHAR2(20),
Std_City VARCHAR2(20),
Std_State VARCHAR2(20),
Std_Zip NUMBER(5),
Std_Major VARCHAR2(10),
Std_Class VARCHAR2(2),
Std_GPA NUMBER(3,2) CONSTRAINT Student_GPA_cc CHECK (Std_GPA<= 4.0 AND Std_GPA>=0));
INSERT INTO Student
VALUES('101','Joe','Smith','Eau Clare','WI', '18121','IS','FR','3.0');
INSERT INTO Student
VALUES('102','Jenny','Sneider','Eau Clare','WI', '98011','IS','JR','3.2');
INSERT INTO Student
VALUES('103','Dan','Robinson','Sartell','MN', '98042','IS','JR','3.5');
INSERT INTO Student
VALUES('104','Sue','Williams','St.Cloud','MN', '56301','ACCT','SR','3.2');
-------------------------------------------------------------------------------
--Offering Table
CREATE TABLE Offering(
Offer_No NUMBER(4) CONSTRAINT Offer_No_pk PRIMARY KEY,
Course_No VARCHAR2(10) CONSTRAINT Course_No_fk REFERENCES Course(Course_No),
Off_Term VARCHAR2(7),
Off_Year NUMBER(4),
Off_Loca VARCHAR2(10),
Off_Time Varchar2(8),
Off_Day VARCHAR2(7),
Fac_SSN NUMBER(4) CONSTRAINT Fac_SSn_fk REFERENCES Faculty(Fac_ID));
INSERT INTO OFFERING
VALUES('2201', 'IS 250', 'Spring', '2000', 'BB260', '10:30am', 'MWF', '9002');
INSERT INTO OFFERING
VALUES('2202', 'IS 250', 'Spring', '1999', 'BB118', '8:00am', 'TTH', '9002');
INSERT INTO OFFERING
VALUES('2203', 'IS 350', 'Fall', '2001', 'BB260', '9:30am', 'TTH', '9001');
INSERT INTO OFFERING
VALUES('2204', 'IS 351', 'Fall', '2001', 'BB315', '12:30pm', 'TTH', '9003');
INSERT INTO OFFERING
VALUES('1101', 'ACCT 291', 'Fall', '2000', 'BB320', '12:30pm', 'MWF', '9010');
INSERT INTO OFFERING
VALUES('2205', 'IS 443', 'Fall', '2002', 'BB216', '12:30pm', 'MWF', '9003');
---------------------------------------------------------------------------------
--Enrollment Table
CREATE TABLE Enrollment(
Std_ID NUMBER(4) CONSTRAINT Enroll_Std_ID_fk REFERENCES Student(std_ID),
Offer_No NUMBER(4) CONSTRAINT Enroll_Offer_No_fk REFERENCES Offering(Offer_No),
Enr_Grade Char(1) CONSTRAINT Enroll_grade_cc CHECK (Enr_Grade IN('A','B','C','D','F')),
CONSTRAINT Enroll_pk PRIMARY KEY (Std_ID,Offer_No));
INSERT INTO ENROLLMENT
VALUES('101', '2201', 'A');
INSERT INTO ENROLLMENT
VALUES('101', '2203', 'B');
INSERT INTO ENROLLMENT
VALUES('102', '2203', 'C');
INSERT INTO ENROLLMENT
VALUES('103', '2203', 'B');
INSERT INTO ENROLLMENT
VALUES('103', '2201', 'C');
INSERT INTO ENROLLMENT
VALUES('103', '1101', 'B');
Here is what I came up with as of now. It still doesn't display the student who hasn't registered for a course. How can I fix this so that it does display sue with 0 courses taken.Thanks.
--Q2
SELECT Count(*) as Num_Courses, Std_FN, Std_LN
FROM Enrollment, Student
WHERE Enrollment.Std_ID = Student.Std_ID
Group by Enrollment.Std_ID, Std_FN, Std_LN
Having Count(*)>= 0;
Your query has an inner join, which means that the Std_ID needs to be in both tables or else the row won't be included. In other words, students with no courses aren't part of the result set that's counted.
Use a left join instead, and count on the Enrollment.Std_ID. If the student doesn't have a matching Enrollment row, then Enrollment.Std_ID will be null so it won't be counted, which results in a zero for that student - and that's what you want.
One more thing: I'm pretty sure Oracle will want you to have Std_ID in the SELECT clause if you're grouping by it.
SELECT COUNT(Enrollment.Std_ID) AS Num_Courses, Student.Std_ID, Std_FN, Std_LN
FROM Student
LEFT JOIN Enrollment ON Student.Std_ID = Enrollment.Std_ID
GROUP BY Student.Std_ID, Std_FN, Std_LN
You need a OUTER Join here. Students having no enrolments would be returned with NULL values from the enrollment table, thus the count of it would be 0. And your modified query is
SELECT Count(Enrollment.Std_ID) as Num_Courses, Std_FN, Std_LN
FROM Enrollment, Student
WHERE Student.Std_ID = Enrollment.Std_ID(+)
Group by Student.Std_ID, Std_FN, Std_LN
Having Count(*)>= 0;

Create a relation

I created the following tables and inserted few values.
create table books
(
bookid integer primary key,
booktitle varchar(20),
year integer,
publisherid integer foreign key references publisher(publisherid),
price integer,
number integer
)
create table publisher
(
publisherid integer primary key,
publishername varchar(20)
)
create table author
(
authorid integer primary key,
authorname varchar(20)
)
create table bookauthor
(
bookid integer foreign key references books(bookid),
authorid integer references author(authorid),
earnings integer
)
create table bookreference
(
bookid integer foreign key references books(bookid),
referencebook varchar(20),
times integer
)
create table reviewer
(
reviewerid integer primary key,
reviewername varchar(20)
)
create table bookreview
(
bookid integer foreign key references books(bookid),
reviewerid integer foreign key references reviewer(reviewerid),
score integer
)
Now, I want to solve following query in SQL Server 2000. Find all the books published in 2003 and reviewed by both ‘Sammer Tulpule’ and ‘Hemant Mahta’ . I am not getting any idea about query. How can I write it?
Thanks,
Pooja
SELECT b.bookid as bookid
FROM (books b INNER JOIN bookreview br ON (br.bookid=b.bookid))
INNER JOIN reviewer r ON (br.reviewerid = r.reviewerid)
WHERE year=2003 AND r.reviewername IN ('Sammer Tulpule', 'Hemant Mahta')
GROUP BY b.bookid
HAVING COUNT(r.reviewerid) >= 2
Obviously, this is assuming that you don't have duplicated entries in the bookreview table.
(SELECT b.* FROM books b, bookreview br, reviewer rv WHERE b.year = 2003 AND br.bookid = b.bookid and br.reviewerid = rv.reviewerid and rv.reviewername = 'Sammer Tulpule')
INTERSECT
(SELECT b.* FROM books b, bookreview br, reviewer rv WHERE b.year = 2003 AND br.bookid = b.bookid and br.reviewerid = rv.reviewerid and rv.reviewername = 'Hemant Mahta')
I am typing this on the fly so there might be small errors, but you get the general idea...

get max from table where sum required

Suppose I have a table with following data:
gameId difficultyLevel numberOfQuestions
--------------------------------------------
1 1 2
1 2 2
1 3 1
In this example the game is configured for 5 questions, but I'm looking for a SQL statement that will work for n number of questions.
What I need is a SQL statement that given a question, displayOrder will return the current difficulty level of question. For example - given a displayOrder of 3, with the table data above, will return 2.
Can anyone advise how the query should look like?
I'd recommend a game table with a 1:m relationship with a question table.
You shouldn't repeat columns in a table - it violates first normal form.
Something like this:
create table if not exists game
(
game_id bigint not null auto_increment,
name varchar(64),
description varchar(64),
primary key (game_id)
);
create table if not exists question
(
question_id bigint not null auto_increment,
text varchar(64),
difficulty int default 1,
game_id bigint,
primary key (question_id) ,
foreign key game_id references game(game_id)
);
select
game.game_id, name, description, question_id, text, difficulty
game left join question
on game.game_id = question.game_id
order by question_id;
things might be easier for you if you change your design as duffymo suggests, but if you must do it that way, here's a query that should do the trick.
SELECT MIN(difficultyLevel) as difficltyLevel
FROM
(
SELECT difficltyLevel, (SELECT sum(numberOfQuestions) FROM yourtable sub WHERE sub.difficultyLevel <= yt.difficultyLevel ) AS questionTotal
FROM yourTable yt
) AS innerSQL
WHERE innerSQL.questionTotal >= #displayOrder

Resources