Database Design for Time Table Generation - mysql

I am doing a project using J2EE(servlet) for Time Table Generation of College.
There are Six Slots(6 Hours) in a Day
4 x 1 HR Lectures
1 x 2 HR Lab
There Are three batches ( 3IT, 5IT, 7IT)
2 Classroom
1 LAB
Each slot in the time table will have
(Subject,Faculty)
For Lab I will duplicate the slot.
The Tables
Subject(SubjectID INT, SubjectName VARCHAR);
Faculty(FacultyID INT,FacultyName VARCHAR,NumOfSub INT,Subjects XYZ);
Here I am not able to decide the DATATYPE for subject. What should I do ? Since a faculty can teach multiple subjects ? Also how to link with Subject Table ?
P.S. Using MySQL Database

You don't want to actually store either NumOfSub (number of subjects) OR Subjects in Faculty. Storing subjects that way is a violation of First Normal Form, and dealing with it would cause major headaches.
Instead, what you want is another table:
FacultySubject
----------------
FacultyId -- fk for Faculty.FacultyId
SubjectId -- fk for Subject.SubjectId
From this, you can easily get the count of subjects, or a set of rows listing the subjects (I believe MySQL also has functions to return a list of values, but I have no experience with those):
This query will retrieve the count of Subjects taught by a particular teacher:
SELECT Faculty.FacultyId, COUNT(*)
FROM Faculty
JOIN FacultySubject
ON FacultySubject.FacultyId = FacultyId.FacultyId
WHERE Faculty.FacultyName = 'Really Cool Professor'
GROUP BY Faculty.FacultyId
... and this query will get all the subjects (named) that they teach:
SELECT Subject.SubjectId, Subject.SubjectName
FROM Faculty
JOIN FacultySubject
ON FacultySubject.FacultyId = FacultyId.FacultyId
JOIN Subject
ON Subject.SubjectId = FacultySubject.SubjectId
WHERE Faculty.FacultyName = 'Really Cool Professor'
(note that this last returns the subjects as a set of rows ie:
SubjectId SubjectName
=========================
1 Tree Houses
2 Annoying Younger Sisters
3 Swimming Holes
4 Fishing
)

Related

How to retrieve hierarchial parent-child data related by multiple tables?

I have a website that contains guitar lessons and exercises, broken down by category. So you could have category scales. Then a lesson scales lesson1, which could contain exercise1_1, exercise1_2. Likewise for other categories and lessons with exercises.
Lessons and exercises are considered nodes (it is a Drupal site). So there is a node table that has node ids, node type (lesson or exercise) and titles.
Other info fields for these nodes (lesson/exercise text, etc) are stored in separate tables for each field. For instance there is a drupal_field_data_description table that contains description for each lesson and exercise.
Categories are stored in a taxonomy term table.
Relations among categories are handled via a taxonomy index table that establishes child-parent relation (so you could have scales, scales->major scales, etc). For my question, I am just considering one depth of category.
Categories of lessons and exercises are stored in a table drupal_field_data_field_category, which maps lessons and exercises to the category they are a part of.
Exercise-Lesson child-parent relations are stored in a table drupal_field_data_field_lesson that maps exercises to lessons.
Here is example data:
The categories (drupal_taxonomy_term_data):
tid vid name
1 2 Scales
2 2 Arpeggios
The lessons and exercises (drupal_node):
nid type title
1 lesson Lesson1
2 lesson Lesson2
3 exercise Ex1_1
4 exercise Ex1_2
5 exercise Ex2_1
6 exercise Ex2_2
The description field for the lessons and exercises (drupal_field_data_field_description):
entity_type bundle entity_id field_description_value
node lesson 1 Lesson1Summary
node lesson 2 Lesson2Summary
node exercise 3 Ex1_1Summary
node exercise 4 Ex1_2Summary
node exercise 5 Ex2_1Summary
node exercise 6 Ex2_2Summary
The mapping of lessons and exercises to the taxonomy (drupal_taxonomy_index):
nid tid
1 1
2 1
3 1
4 1
5 1
6 1
The mapping of lessons and exercises to the category (drupal_field_data_field_category) (this one almost seems unnecessary because of the taxonomy index):
entity_type bundle entity_id field_category_tid
node lesson 1 1
node lesson 2 1
node exercise 3 1
node exercise 4 1
node lesson 5 1
node lesson 6 1
The mapping of exercises to lessons (drupal_field_data_field_lesson):
entity_type bundle entity_id field_lesson_target_id
node exercise 3 1
node exercise 4 1
node exercise 5 2
node exercise 6 2
So... with this structure, I can't figure out how to build a query that will return a result of the form
Lesson1 Lesson1Summary
Ex1_1 Ex1_1Summary
Ex1_2 Ex1_2Summary
Lesson2 Lesson2Summary
Ex2_1 Ex2_1Summary
Ex2_2 Ex2_2Summary
Note that Lesson1 and Lesson2 are in the same category.
I need to return such data, because for a category page (that has no subcategories), I need to display a table for each lesson that shows the exercises in the lesson.
I could do all this in multiple queries, but I am really trying to better understand SQL joins and grouping. Also, I am not dead set on a result set as shown above. I am open to whatever result set will let me readily display the data (which I will do via PHP) in the fashion as I described.
The SQL fiddle is here
How would you recommend building such a query to extract a lesson and its exercises grouped in a logical way (e.g. how I show above)?
Seems getting lesson and exercises in this way would amount to a self join, with a variety of inner joins on the other tables but I just can't piece it all together...
Well, after much reading, I think I figured it out:
SELECT n.title, d.field_description_value, n.nid, l.field_lesson_target_id from drupal_node n
JOIN drupal_field_data_field_description AS d ON d.entity_id = n.nid
JOIN drupal_taxonomy_index AS t ON t.nid = n.nid
LEFT JOIN drupal_field_data_field_lesson AS l ON l.entity_id = n.nid
ORDER BY COALESCE(l.field_lesson_target_id, n.nid), l.field_lesson_target_id, n.nid
I based above on this post
My sqlfiddle is here.
This is definitely new territory for me, and while above works, I wish I understood the ORDER BY and GROUP BY nuances to understand where/how to use them.

Relational Database Logic

I'm fairly new to php / mysql programming and I'm having a hard time figuring out the logic for a relational database that I'm trying to build. Here's the problem:
I have different leaders who will be in charge of a store anytime between 9am and 9pm.
A customer who has visited the store can rate their experience on a scale of 1 to 5.
I'm building a site that will allow me to store the shifts that a leader worked as seen below.
When I hit submit, the site would take the data leaderName:"George", shiftTimeArray: 11am, 1pm, 6pm (from the example in the picture) and the shiftDate and send them to an SQL database.
Later, I want to be able to get the average score for a person by sending a query to mysql, retrieving all of the scores that that leader received and averaging them together. I know the code to build the forms and to perform the search. However, I'm having a hard time coming up with the logic for the tables that will relate the data. Currently, I have a mysql table called responses that contains the following fields,
leader_id
shift_date // contains the date that the leader worked
shift_time // contains the time that the leader worked
visit_date // contains the date that the survey/score was given
visit_time // contains the time that the survey/score was given
score // contains the actual score of the survey (1-5)
I enter the shifts that the leader works at the beginning of the week and then enter the survey scores in as they come in during the week.
So Here's the Question: What mysql tables and fields should I create to relate this data so that I can query a leader's name and get the average score from all of their surveys?
You want tables like:
Leader (leader_id, name, etc)
Shift (leader_id, shift_date, shift_time)
SurveyResult (visit_date, visit_time, score)
Note: omitted the surrogate primary keys for Shift and SurveyResult that I would probably include.
To query you join shifts and surveys group on leader and taking the average then jon that back to leader for a name.
The query might be something like (but I haven;t actually built it in MySQL to verify syntax)
SELECT name
,AverageScore
FROM Leader a
INNER JOIN (
SELECT leader_id
, AVG(score) AverageScore
FROM Shift
INNER JOIN
SurveyResult ON shift_date = visit_date
AND shift_time = visit_time --depends on how you are recording time what this really needs to be
GROUP BY leader ID
) b ON a.leader_id = b.leader_id
I would do the following structure:
leaders
id
name
leaders_timetabke (can be multiple per leader)
id,
leader_id
shift_datetime (I assume it stores date and hour here, minutes and seconds are always 0
survey_scores
id,
visit_datetime
score
SELECT l.id, l.name, AVG(s.score) FROM leaders l
INNER JOIN leaders_timetable lt ON lt.leader_id = l.id
INNER JOIN survey_scores s ON lt.shift_datetime=DATE_FORMAT('Y-m-d H:00:00', s.visit_datetime)
GROUP BY l.id
DATE_FORMAT here helps to cut hours and minutes from visit_datetime so that it could be matched against shift_datetime. This is MYSQL function, so if you use something else you'll need to use different function
Say you have a 'leader' who has 5 survey rows with scores 1, 2, 3, 4 and 5.
if you select all surveys from this leader, sum the survey scores and divide them by 5 (the total amount of surveys that this leader has). You will have the average, in this case 3.
(1 + 2 + 3 + 4 + 5) / 5 = 3
You wouldn't need to create any more tables or fields, you have what you need.

How to correctly normalize data set in to tables?

I have a table where first column refers user name other columns refer each subjects that student does and their skill level for each subjects. These subjects are belong to several main subjects streams such as Mathematics, Art and etc.
I normalize the data in to following tables.
Subject_Stream
id Main_Stream Subject
1 Math pure-maths
2 Math applied-maths
3 Art Dancing
4 Art Music
Student_table
id name subject skill_level
1 xxx Music 5
2 xxx Pur-math 4
3 xxx Applied-math 1
4 yyy Music 3
select subjects, skill_level where usename="xxx" order by Desc
I can use a similar query to get subjects and their skill level for any given student.
Apart from, this I need to select students when I select any subject or collection of subjects that do.And print their Name, subjects, skill level in descending order.
Here any student can do different subjects in different streams too.
But I can't easily get this result from my existing tables. How to do this correctly?
Do I need to improve database schema?
First I want to suggest two small improvement for your table structure to better normalize the data. Your table Student_table would be slighly better organized if
instead of name you reference the user_id from your studends-table (that means the name of the student is only present in your studends-table - remember, names can change, p.e. marriage) and
instead of the name of the subject you reference the id of the subject - therefore the name of the subject is only present in your table Subject_Stream.
Your table Student_table would then look something like this:
id user_id subject_id skill_level
--------------------------------
1 1 4 5
2 1 1 4
3 1 2 1
4 2 4 3
Based on this you should be able to get every query you describe. For specifics please update your question and add some examples how your desired results look like, then I'll look into it and give you a few examples.
Apart from, this I need to select students when I select any subject
or collection of subjects that do. And print their Name, subjects,
skill level in descending order.
For a single subject . . .
select name, subject, skill_level
from Student_table
where subject = 'your subject name'
order by skill_level desc, name asc
For multiple subjects . . .
select name, subject, skill_level
from Student_table
where subject = 'some subject name'
or subject = 'some other subject name'
order by skill_level desc, name asc
There are other ways to express this kind of query, but I think multiple OR clauses is the easiest to understand at first.
To select by stream (Math, in this case) . . .
select Student_table.name
, Student_table.subject
, Student_table.skill_level
, subject_stream.main_stream
from Student_table
inner join subject_stream
on subject_stream.subject = student_table.subject
where subject_stream.main_stream = 'Math'
order by Student_table.skill_level desc, name asc

Is it possible to construct a single query to match two tables enforcing a 1:1 relationship between them?

For those interested in the reasoning behind this question: I have an e-commerce site that works fine, but has no gift certificate capabilities. Adding monetary GCs should be pretty simple, but I'd also like to allow the gifting of specific products (sounds odd but is relevant to my industry). So I plan to create a new table to house gift certificates that are linked to a specific user and product, and I need an efficient way to evaluate that table on the cart and checkout pages.
Imagine tables exist that look similar to the following:
CartContents
CartID Integer (Unique sequential row identifier)
UserID Integer
ProductID Integer
Quantity Integer
Gifts
GiftID Integer (Unique sequential row identifier)
ProductID Integer
UserID Integer
Quantity Integer
This is an overly simplified layout, but demonstrates the idea. The first table lists items in the user's cart; one record per product (though real products will have additional details that may vary). The product table has further attributes on products but I don't list it here for simplicity. The second table is a set of gift certificates, each for a specific product, that have been presented to this user ID.
Table data may look like the following:
CartContents
CartID UserID ProductID Quantity
1 1 1 1
2 1 2 2
3 1 1 2
4 2 3 1
Gifts
ProductID UserID Quantity
1 1 1
2 1 1
3 3 1
Is it possible to construct a single query that provides one row per cart item and links the above two tables taking into account that each gift may only link to each cart item once? Or does this need to be handled in a script?
In other words, because user 1 has product 1 in their cart twice, and they have only been promised one free product 1, the query should return a matching Gifts record for cartID 1, but not cartID 3. The query, pulling for user ID 1, would return:
CartID ProductID Quantity unpaidQuantity
1 1 1 0
2 2 2 1
3 1 2 2
Or
CartID ProductID Quantity unpaidQuantity
1 1 1 1
2 2 2 1
3 1 2 1
I realize that the fact that there is more than one 'right' answer to this question raises a red flag. In reality it doesn't matter which cart record each GC is applied to, as the end result (the price) will work out the same. I'm perfectly happy to say the 'first' (lowest cartID) is the one that should be linked.
My assumption is that the database will be far more efficient at this than any script I could write; I'd even be willing to bet there's some crazy type of join I've never heard of specifically designed for it. I am also assuming that any such ColdFusion script may be somewhat complicated and thus take a fair amount of development and testing time while a single query may be relatively simple (though apparently beyond my limited SQL capabilities). If I'm incorrect in this I'd appreciate any thoughts on that as well.
My setup, if it matters:
MySQL 5.0
ColdFusion 9
Windows 2000 AS
Edit:
It sounds like the quantity column is really going to cause issues, so let's continue assuming that quantity does not exist on the Gifts table. It still must exist on cartContents, however.
I thought of another way of doing this that just requires and additional group by and join. However, it requires a unique id on CartContents. I'm not sure i this is what CartId is supposed to be. However, it seems that a user could have more than one cart, so I assume not.
The idea is to identify the first record for a given product in each cart. Then, use this information when joining in the gifts.
select CartID, UserID, ProductID, Quantity, FirstCCId
from CartContents cc join
(select CartID, UserID, ProductID, min(CartContentsId) as FirstCCId
from CartContents cc
group by CartID, UserID, ProductID
) ccmin
on cc.CartId = ccmin.CartId and cc.UserId = ccmin.UserId and
cc.ProductId = ccmin.ProductId left outer join
Gifts g
on cc.ProductID= g.ProductId and cc.UserID = g.userId and
cc.CartContentsId = ccmin.FirstCCId
This works when the gift is applied to only one product line row. If the quantity for the gift is actually larger than the quantity on any given line, this query still only puts it on one line.
Does this work?
select c.cartid, c.productid, c.quantity, c.quantity -
case
when (select sum(c2.quantity) from CartContents c2
where c.userid = c2.userid
and c.productid = c2.productid
and c.cartid < c2.cartid) <
(select g.quantity from gifts g
where c.userid = g.userid
and c.productid = g.productid) then
(select g.quantity from gifts g
where c.userid = g.userid
and c.productid = g.productid) -
(select sum(c2.quantity) from CartContents c2
where c.userid = c2.userid
and c.productid = c2.productid
and c.cartid < c2.cartid)
else 0
end UnpaidQuantity
from CartContents c
where userid = 1

Need help on database designing

I am currently out of idea how to design this table so I would really like some suggestions. Description as follows:
Table will hold 3 exam result.
Exam A: 8 mandatory subject with infinite optional subject.
Exam B: 6 mandatory subject with infinite optional subject.
Exam C: 1 mandatory subject with 4 optional subject.
Feature to keep in mind:
Each subject's result need to be searchable (eg: Find A for Math in Exam A)
Basic total calculation (eg: calculate how many As in Math for Exam A)
Just inserting data I would be able to think of something however when putting the features into the mix, it just won't work.
My last resort is having a single table with: studentid, exam, subjectcode, result. This will work as in searchable and calculable however I have a feeling of a very messy and huge database in the long run.
My current design (given by my friend):
Each subject and its result have its own field. It works but very hard to expand (add more subjects).
Any recommendations?
Possible table structure (leaving out column definitions):
Exam
---------
Exam_ID
Exam_name
number_of_req_subjects
number_of_opt_subjects <---- -1 could be infinite
Subject
-----------
Subject_id
subject_name
exam_subject
------------
exam_subject_id
exam_id
subject_id
required
exam_result
------------
exam_result_id
exam_subject_id
result
To get the number of A's for Math in Exam A:
SELECT count(exam_result_id)
FROM exam_result er, exam_subject es, subject s, exam e
WHERE er.exam_subject_id = es.exam_subject_id
AND es.subject_id = s.subject_id
AND es.exam_id = e.exam_id
AND e.exam_name = 'A'
AND s.subject_name = 'MATH'
(I know using joins would be better than where to join the different tables, but I'm being a bit lazy).
The subjects result being searchable...we don't have enough information. In fact, my answer may be completely off, but is as close as I think I can get it with the given information at the moment. Making something searchable is just a matter of creating a sufficiently useful select statment.