Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I have a database which contains main symptoms, additional symptoms and diseases. I need to replace 2 queries because I am sure my first query is not efficient and my 2nd is not correct at all. I hope anyone can help me because I am new to this area..
Database explanation:
The database is being used by a medical app:
The user selects a specific bodypart
The app lists all main symptoms of that specific bodypart
The user selects a main symptom (common or less common)
The app lists all diseases of the selected main symptom. There also appear 2 checkboxes (additional symptoms) that can be checked by the user. The order of the listed diseases (d_weight) depends on age, gender, selected main symptom and what boxes the user has checked. A disease with d_weight <= 5 is considered to be a common disease. A disease with d_weight > 5 is considered to be less-common. The possibilities of user input (age, gender, bodypart, main symptom) is stored in the symptom_disease_combi table.
asa_id is the id of all symptom that apply (addtional symptoms that are checked by the user)
asc_id is the id of all possibilities of additional symptoms that belong to the specific main symptom. For example, asc_id = 0, in case there is no additional symptom selected. asc_id = 1, in case only additional symptom 'Insomnia' is selected. asc_id = 2, in case both 'Insomnia' and 'Blowing up' are selected.
1. Query to get all symptoms of a specific bodypart (can be improved):
SELECT DISTINCT s.name
, s.id
, sdc.s_common
FROM symptom as s
, symptom_disease_combi as sdc
WHERE sdc.age = ".$age."
AND sdc.gender = ".$gender."
AND sdc.bodypart = ".$bodypart."
AND sdc.s_id = s.id
2. Query to get all diseases and additional symptoms of selected symptom (doesn't work):
SELECT DISTINCT d.name
, d.id
, sdc.d_weight
, adls.id
, adls.name
FROM disease as d
, symptom_disease_combi as sdc
, symptom as s
, adlsymptom as adls
WHERE sdc.age = ".$age."
AND sdc.gender = ".$gender."
AND sdc.bodypart = ".$bodypart."
AND s.id = ".$sid."
AND sdc.s_id = s.id
3. Database structure (please let me know if my design can be improved)
CREATE TABLE symptom
(id INT NOT NULL AUTO INCREMENT
,name VARCHAR(100) DEFAULT NULL
,critical INT NOT NULL
,PRIMARY KEY (id)
) ENGINE=MyISAM;
id name critical
1 Behavioral disturbances 1
2 Ear pressure 0
3 Elevated temperature 0
4 Fainting 0
5 Head pain 0
CREATE TABLE disease (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(100) DEFAULT NULL,
critical int(11) NOT NULL,
description text NOT NULL,
tests text NOT NULL,
treatment text NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=latin1
id name critical description tests treatment
1 Adjustment disorder 0 lorem ipsum lorem ipsum lorem ipsum
2 ADHD 0 lorem ipsum lorem ipsum lorem ipsum
3 Drug reaction 0 lorem ipsum lorem ipsum lorem ipsum
4 Seizure (epilepsy) 1 lorem ipsum lorem ipsum lorem ipsum
CREATE TABLE adlsymptom (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
id name
1 Insomnia
2 Blowing up
3 Depressed
4 Drug abuse
CREATE TABLE adlsymptom_apply (
id int(11) NOT NULL,
as_id int(11) NOT NULL,
PRIMARY KEY (id,as_id),
KEY FK_additional_symptom_that_apply (as_id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
id as_id
1 1
1 2
CREATE TABLE adlsymptom_combi (
id int(11) NOT NULL,
as_id int(11) NOT NULL,
PRIMARY KEY (id,as_id),
KEY FK_additional_symptom_combination (as_id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
id as_id
1 1
2 2
3 1
3 2
CREATE TABLE symptom_disease_combi (
id int(11) NOT NULL AUTO_INCREMENT,
age int(11) NOT NULL,
gender int(11) NOT NULL,
bodypart int(11) NOT NULL,
s_id int(11) NOT NULL,
s_common int(11) NOT NULL,
asc_id int(11) NOT NULL,
asa_id int(11) NOT NULL,
d_id int(11) NOT NULL,
d_weight int(11) NOT NULL,
PRIMARY KEY (id),
KEY FK_symptom (s_id),
KEY FK_additional_symptom_combination (asc_id),
KEY FK_additional_symptom_that_apply (asa_id),
KEY FK_disease (d_id)
) ENGINE=MyISAM AUTO_INCREMENT=65 DEFAULT CHARSET=latin1
id age gender bodypart s_id s_common asc_id asa_id d_id d_weight
1 1 1 1 1 1 0 1 1 1
2 1 1 1 1 1 0 1 2 2
3 1 1 1 1 1 0 1 3 3
4 1 1 1 1 1 0 1 11 4
UPDATE 1:
critical in disease and symptom is created to tell the user that they need to go to the hospital immediately, when they click on the disease or symptom
age, gender, bodypart are id's, so age = 1, means 0-5, age = 2 means 6-17, age = 3 means 18-59 and age = 4 means 60+.
Please take a look at the design of the application, it will help a lot to understand the design of the database: http://i.imgur.com/p9QP1av.png Btw, in the design; cause == disease...
asa_id refers to id of adlsymptom_apply
asc_id referst to id of adlsymptom_combi
The 'distinct' was used to get all symptoms/diseases just 1 time. I am sure it's not needed, but I don't know how to fix it.
ditch 2 symptom tables, go with one (symptom) and 1 intersect table (sdc)
i would not add a new column to symptom, such as status/level trying to jack up a symptom
in importance to main or secondary, despite the temptation, because this could easily
make it not flexible.
for instance, fainting may seem main for once disease/condition but it could skew it in general
go for generality, therefore 1 table for symptoms, you correctly have a d_weight in sdc
i like your sdc.d_weight concept.
for instance, fainting might have some weight with epilepsy, but not for the flu. the whole concept
is screwed up when someone with the flu is prescribed Zarontin and not Tamiflu
since i like your sdc.d_weight concept, i wonder why you went with the additional symptoms table
in table sdc, you have keys/indexes with names starting with "FK_". Hopefully you have actual FK constrainst
and not just naming conventions that make you think you have them (FK's, you don't have them)
for instance, real FK's for bodypart, symptom, and disease/condition.
as the user selects symptoms, remove them from the GUI's ability to add that symptom again for search
this cuts down work and simplifies querying by ditching the secondary table (line 1 of what i wrote and suggest)
note, again, that your use of KEY (which is a synonym for index, not foreign key constraint), just creates
an index ...
create table symptom
(
id int not null auto_increment primary key,
name varchar(100) not null, -- if you can't put a name to it, don't have it
critical int not null
)ENGINE=MyISAM default charset=latin1;
create table disease
(
id int not null auto_increment primary key, -- don't mix and match int with int(11)
name varchar(100) not null, -- if you can't put a name to it, don't have it
critical int not null,
-- etc columns, text
)ENGINE=MyISAM default charset=latin1;
.
create table symptom_disease_combi
( -- a.k.a. sdc
id int not null auto_increment primary key, -- don't mix and match int with int(11)
age int not null,
gender char(1) not null, -- int(11) is overkill
bodypart int not null,
s_id int not null,
s_common int not null,
asc_id int not null,
asa_id int not null,
d_id int not null,
d_weight int not null,
-- additional indexes (note pk above, so it is there and happy)
-- note that some of your indexes (your KEYS) are worthless for the queries in question
-- however they may be useful for other queries in your system
-- for instance your asc and asa indexes will not be useful as they won't be picked up
-- in relation to the question posed
--
-- you will need to find the proper balance of index bloat based on system needs
INDEX idx_sdc_siddid (s_id,d_id,bodypart), -- composite, general purpose
INDEX idx_sdc_didsid (d_id,s_id,bodypart), -- ditto but reverse
INDEX idx_sdc_ascid (asc_id),
INDEX idx_sdc_asaid (asa_id),
-- put constraints here:
CONSTRAINT `fk_sdc_bodypart` FOREIGN KEY (bodypart) REFERENCES bodypart(id),
CONSTRAINT `fk_sdc_sid` FOREIGN KEY (s_id) REFERENCES symptom(id), -- don't mix and match int and int(11)
CONSTRAINT `fk_sdc_did` FOREIGN KEY (d_id) REFERENCES disease(id)
-- are there others, such as asc asa tables ??
)ENGINE=MyISAM default charset=latin1;
i think you need to think long and hard about the age column
is sdc going to be loaded with a new row, one for each age, 50 to 120, for gender='M', for prostrate cancer?
i am basing this on your age=xxxxx line, maybe you mean >=. or for leukemia, <= ... i think
your schema needs a bit of thought
so i am excluding age from the below query
-- for the next query, i wonder why you need a distinct?
-- is there more than 1 sdc.s_common for given symptom row ? only you would know
-- if not, ditch the distinct
-- the order of your join on clause matters, make it follow a good index so results come back fast
-- i am not suggesting these are covered indexes, but you will minimize table scan activity
select distinct s.name, s.id, sdc.s_common
from symptom s
join symptom_disease_combi sdc
on sdc.s_id=s.id and sdc.bodypart=".$bodypart." and sdc.gender= ".$gender."
-- not my comment above about age
-- incorporate weights as needed in a computed column
those are my suggestions, and good luck
Related
I am new to database design. I am using MySQL
I have a Form table which contains a field Country
Since the values in this columns can be one from the country list only, so I made this column int instead of varchar and put the list in a new table. shown as below:
Instead of having country column like
country varchar(255) NOT NULL
I made it countryCode with datatype INT and added a Foreign Key to table CountryMaster which is shown as below
...
countryCode INT NOT NULL,
FOREIGN KEY (countryCode) REFERENCES CountryMaster (countryCode),
...
create table CountryMaster(
countryCode INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255)
);
It is working fine, but the issue is that the same table contains many more column like formStatus (with possible values Draft, Saved, Approved), gender (Male, Female), bloodGroup (A, B, O), rHFactor (+ve, -ve), occupation (Service, Business, Student, Unemployed) etc. If I will make separate table for each such column I will end up in 20 to 25 Master tables.
I also tried to put all such Master Date in a single table i.e. CommonMaster with columns type, key and value shown as below:
create table CommonMaster(
commonCode INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
type VARCHAR(255) NOT NULL,
key INT NOT NULL,
value VARCHAR(255) NOT NULL
);
with values like:
code type key value
7 BLOOD_GROUP 1 A
8 BLOOD_GROUP 2 B
9 BLOOD_GROUP 3 O
10 RH_FACTOR 1 +ve
11 RH_FACTOR 2 -ve
12 OCCUPATION 1 Service
13 OCCUPATION 2 Profession
14 OCCUPATION 3 Business
15 OCCUPATION 4 Student
16 OCCUPATION 5 Unemployeed
I want to know that what is a better approach and why. I also have to use hibernate orm in the project. So the design should be pertaining to that.
I prefer a hundreds master tables than a common table with all the types; more structure, more organization, more adaptive for add, delete and update the value of then, also for reports.
I'm very beginner at MySQL and have just started to play around with foreign keys and INNER JOIN operator.
So I've made a few tables like:
CREATE TABLE `models`
(
`id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR (255) NOT NULL,
`price` MEDIUMINT UNSIGNED NOT NULL,
PRIMARY KEY( `id` )
);
CREATE TABLE `vendors`
(
`id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR (255) NOT NULL,
`id_model` TINYINT UNSIGNED NOT NULL,
PRIMARY KEY( `id` ),
FOREIGN KEY (`id_model`) REFERENCES models(`id`)
);
CREATE TABLE `cars`
(
`serial_number` MEDIUMINT UNSIGNED NOT NULL,
`id_vendor` TINYINT UNSIGNED NOT NULL,
FOREIGN KEY (`id_vendor`) REFERENCES vendors(`id`),
PRIMARY KEY( `serial_number` )
);
I know how to get output with that. However, the problem is that I don't know how to insert data properly. All I can do is insert data table by table. But how to do it in one query, and if I am inserting Honda Civic and already have Honda Accord, for example, wouldn't it duplicate Honda vendor in the database?
It seems that the structure of the database is not really coherent. Maybe I don't understand what you are trying to do, but ... anyway, here goes.
Assuming that what you want to do is store a list a of cars in a properly normalized relational database, first thing you want to do is think what is happenning in "real life":
Manufacturers (vendors) make/sell cars
Manufacturers (vendors) make different models of cars
Cars have a model (and a serial number in your case)
Models belong to a vendor (manufacturers)
Considering this, your table structure is:
Vendors
- id
- name
Models
- id
- name
- vendor ( foreign key => vendor.id )
Cars
- id
- serial_number
- model ( foreign key => model.id )
You don't need to have a reference to the vendor in the cars table becoause you have a reference to the model, which in turn has a reference to the vendor.
Whe inserting, you do it one by one, making sure that the foreign key entries already exist.
When you insert a car object, you just need to provide the model id.
When you insert a model object you need to provide a vendor id.
So the Honda Civic/Accord situation does not duplicate Honda. The Tables should be something like this:
Vendor
id, name
1, "Honda"
Model
id, name, vendor
1, "Civic", 1
2, "Accord", 1
Cars
id, serial_no, model
1, "A serial", 2 -> a honda accord
2, "Another serial", 1 -> a honda civic
Hope this helps somewhat.
You do need to check if duplicated record exists yourself.
IF EXISTS (SELECT * FROM vendors WHERE Name = 'Honda')
BEGIN
-- Insert into cars with existing vendor id
END
ELSE
BEGIN
IF EXISTIS (SELECT * FROM models WHERE Name = 'your model name')
BEGIN
-- insert into vendors with existing model id
END
ELSE
BEGIN
-- insert into models
-- insert into vendors
-- insert into cars
END
END
You can create stored procedure for it and pass car, vendor and model as parameters.
Or you can list models first, insert them; then all vendors and all cars. Just a silly answer. Welcome more sophisticated solutions.
I'm creating a page where I want users to be able to book a seat for an event.
1 user can only book 1 seat
users have no seat selected upon login, first after buying into a spot
Need to able to clear seats table, without loosing anything from user-table (except of course the assigned seats.)
I've created two tables, and since I'm pretty new to mySQL, I wanted to check if this was done correctly:
members-table:
user_id int(8) Not null auto_increment
user_name varchar(30) Not null
user_pass varchar(255) Not null
seat_ID smallint(6) Yes NULL
seats-table
seat_ID smallint(6) No auto_increment
user_id smallint(6) Yes NULL
seat_status tinyint(4) Yes NULL
seat_status tinyint(4) Yes NULL
I've created 2 FK-refs:
ALTER TABLE seats
ADD CONSTRAINT FK_seats
FOREIGN KEY (user_id) REFERENCES members(user_id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE seats
ADD CONSTRAINT FK_seats
FOREIGN KEY (seat_ID) REFERENCES members(seat_ID)
ON UPDATE CASCADE
ON DELETE CASCADE;
Am I on the right track? Will I be able to progress to a decent final product with this setup? suggestions/improvements? I don't want to start all over in a couple of weeks because the database structure is of poor quality.
First of all I don't see why you're using a second table if any user can only hold one seat at any given time, secondly user_id in seats-table should be the same size as user_id in members table namely int(8), otherwise you won't be able to seat users after a while, third issue is the duplication of seat_status, I suppose that was a mistake or you had another name for it.
In my opinion a better idea is to use a single table if it's a 1->1 mapping and define it as
CREATE TABLE `members-table` (
user_id int(8) not null auto_increment,
user_name varchar(30) not null,
user_pass varchar(255) not null,
seat -- your type choice, should be nullable if not seated
);
Clearing the seats with this config would be as simple as
UPDATE `members-table` SET `seat` = NULL;
CREATE TABLE `seats` (
id int(4) unsigned not null auto_increment primary key,
row int(2) unsigned not null,
col int(2) unsigned not null,
UNIQUE(row, col)
) ENGINE InnoDB;
CREATE TABLE `members` (
user_id int(8) not null auto_increment primary key,
user_name varchar(30) not null,
user_pass varchar(255) not null,
seat int(4) unsigned null,
FOREIGN KEY(seat) references seats(id) on delete set null on update restrict,
UNIQUE(seat)
) ENGINE InnoDB;
You will have to populate the seats database with all available rows and columns, use null on id when inserting to use the auto_increment feature!
Check if a seat is taken
SELECT COUNT(*) AS occupied FROM members WHERE seat = (SELECT id FROM seats WHERE row = :ROW AND col = :COL);
Alternatively use SELECT (1 - COUNT(*)) AS vacant in the query above if it's more conveninent for you.
Find first free seat
SELECT MIN(id) FROM seats WHERE NOT EXISTS( SELECT seat FROM members WHERE seat = seats.id);
Unassign all taken seats
UPDATE members SET seat = NULL;
THE SQL THAT BUILDS THE TABLES,
--
-- Table structure for table `careers`
--
CREATE TABLE IF NOT EXISTS `careers` (
`career_id` int(11) NOT NULL auto_increment,
`career_name` varchar(75) NOT NULL,
`career_desc` text NOT NULL,
`degree_needed` enum('Yes','No') NOT NULL,
`useful_info` text,
`useful_links` text,
PRIMARY KEY (`career_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=34 ;
-- --------------------------------------------------------
--
-- Table structure for table `course`
--
CREATE TABLE IF NOT EXISTS `course` (
`course_id` int(11) NOT NULL auto_increment,
`course_type` varchar(75) NOT NULL,
`course_names` text NOT NULL,
`extra_needed` enum('Yes','No') default NULL,
`course_link` varchar(150) NOT NULL,
`grades_grade_id` int(11) NOT NULL,
PRIMARY KEY (`course_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=87 ;
-- --------------------------------------------------------
--
-- Table structure for table `grades`
--
CREATE TABLE IF NOT EXISTS `grades` (
`grade_id` int(11) NOT NULL auto_increment,
`grade_desc` text NOT NULL,
`careers_career_id` int(11) NOT NULL,
PRIMARY KEY (`grade_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=87 ;
-- --------------------------------------------------------
An overview of my theory behind the tables, is that each grade is associated with a career and one career can have many grades, from that one course is only associated to one course, but a user may need to do an extra course if the one they pick is not accredited highly enough.
So my question is how do I select the course details for the higher level courses if the user selects a low level course,
for example the user wants to be an electrician, and they have 2 D grades in school this means they can only do a level 2 course, this means that to complete the course they have to do a higher level course. I need to be able to show what the other courses are based on the fact they have selected electrician and a level 2 course, it is worth noting that courses that require extra work have a field 'extra_needed` that is marked as yes.
I cannot for the live or me work out how to get the right data out, I have tried the following,
SELECT *
FROM `course` , `grades` , `careers`
WHERE `course`.`extra_needed` IS NULL
AND `grades`.`grade_id` = `careers`.`career_id`
AND `careers`.`career_id` =6
however this brings back 59 rows of data where as it should bring back 2 rows of data, the other to rows of data that the user could select if they chose the other grade choices.
Looks to me like you are joining on the wrong fields, the relationships look like they would be as follows:
careers.career_id = grades.careers_career_id
grades.grade_id = course.grades_grade_id
so for all courses related to career.career_id = 6 the query would look as follows:
select course.*
from course,
careers,
grades
where course.grades_grade_id = grades.grade_id
and grades.careers_career_id = careers.career_id
and careers.career_id = 6
You would need a more complex query to do what you originally asked though which would involve specifying not only a career_id but also a course_id and then a conditional statement to say whether any further courses are required but I'm not sure if you have all the fields necessary to do this as you would need to know the relationship between the course they have selected and all other courses pertaining to the relevant career. If you simply wish to see all the other courses relating to that career then you would add a line like:
and course.course_id <> (The course they have selected)
If there are only ever two levels of courses then you could add a line like below as if they have selected the higher level it can't satisfy both the last statement and this one whereas if they have selected the lower level both will be true:
and course.extra_needed IS NULL
Replace your query by this one:
SELECT *
FROM careers AS c
LEFT JOIN grades AS g ON g.careers_career_id = c.career_id
LEFT JOIN course AS crs ON crs.grades_grade_id = g.grade_id
WHERE c.career_id =6
AND crs.extra_needed IS NULL
It should work,
Good luck
I'm trying to create a database that stores the students grade for each homework assignment I want to be able to store the grade and how many times that student got a certin grade for example a student got an A+ 30 times, for each student, as well as how many grades a student got. And how much a grade is worth in points for example an A is worth 4.3.
So I was wondering what is the best way to build my MySQL database what do I need to add and what do I need to drop and how can I store numbers like 4.3 for example.
My database so far
CREATE TABLE grades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
grade INT UNSIGNED NOT NULL,
student_work_id INT UNSIGNED NOT NULL,
student_id INT UNSIGNED NOT NULL,
date_created DATETIME NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE work (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
student_id INT UNSIGNED NOT NULL,
title TEXT NOT NULL,
content LONGTEXT NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS student (
id int(8) NOT NULL auto_increment,
student varchar(20) NOT NULL,
PRIMARY KEY (`id`)
)
example of output.
student | grade | count
1 A 10
1 C 2
1 F 4
2 B 20
2 B- 3
2 C+ 10
student | grade | points
1 A 4.3
2 B+ 3.3
3 B- 2.7
4 D+ 1.3
student | total grades
1 90
2 80
3 100
4 1
I think a student - grade - assignment schema would be ok. You should count the number of grades of a student with a query and not maintain that information in your database.
At a conceptual level (not strict mysql), I would do something like this.
table student (
id int (primary key)
varchar name
... // other student attributes (e.g. phone number)
)
table grade (
id int (primary key)
name varchar // e.g. "A+", "B-", etc.
float points // grade value in points
)
table assignment (
id int (primary key)
name varchar // assignment name
student_id int (foreign key) // student that completed the particular assignment
grade_id int (foreign key) // grade achieved in particular assignment
... // other assignment attributes (e.g. due date)
)
Many would suggest that you have a student table, a grade table and a table that relates the student and grade table. You could then do counts based on the table that relates the two with a procedure. Many believe that having a grade and a student in the same table is poor relational design.
Unless you want to use a view, which is really not necessary in this case, you wouldn't store the amount of times a student got a certain grade; rather, you would make a query that gave you that information for each student when you needed it. One example using your current schema, to tell you how many times a particular student having id st_id got each grade, would be like
SELECT COUNT(1) FROM grades WHERE student_id = st_id GROUP BY grade
This will look at all the rows where student st_id got each grade and count them up separately.
As far as storing a mapping from letter grade to the amount of points that its worth, you could do one of two things - either have a table mapping from letter grade to point amount, or just store the point amounts directly. Personally, I think the former is a little bit cleaner as it allows you to change point amounts if the scale changes (not that that should happen often).
Other than that, I think you're doing pretty good. The only thing I would say is that if there is always a one-to-one mapping between grade and work, I might make grade part of the work table rather than separate them. That is, only if there isn't information in the grade table that doesn't belong in the work table.
Looking at your tables it appears that there's some normalizing that you can do, or I might be misunderstanding the objective. I would assume that students and work items are only connected by a grade, which would make your grades essentially a cross-reference table. As a result, you wouldn't reference the student_id column within the work table. Off the cuff, I would change it such that the tables look more like this:
CREATE TABLE student (
student_id int(8) NOT NULL auto_increment,
student varchar(20) NOT NULL,
PRIMARY KEY (student_id)
);
CREATE TABLE work (
work_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
title TEXT NOT NULL,
content LONGTEXT NOT NULL,
PRIMARY KEY (work_id)
);
CREATE TABLE grades (
grade_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
grade decimal(4,2) UNSIGNED NOT NULL,
work_id INT UNSIGNED NOT NULL,
student_id INT UNSIGNED NOT NULL,
date_created DATETIME NOT NULL,
PRIMARY KEY (id)
);
Note that I changed the grade column to a decimal (I'm a SQL Server guy, so MySQL might be different), but any type that can handle non-integer numbers works. With that design, you can then pull your queries as needed to get the types of output you need.
Josh
assuming -
there's more than one student
there's more than one assignment / piece of work to be graded against
more than one student can complete a specific set work/assignment
only one grade can be earned per assignment
I'd go with -
a table for the definition of each set piece of work / a specific assignment.
a table to define each student
a third table to record the result of each assignment per student, ie to store the grade per assignment.
eg -
CREATE TABLE work (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
student_id INT UNSIGNED NOT NULL,
assignment_id int unsigned not null,
grade DECIMAL(1,1)
gradedesc VARCHAR(1)
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS student (
id int(8) NOT NULL auto_increment,
student varchar(20) NOT NULL
PRIMARY KEY (`id`)
)
Create table assignment (
id int(8) not null auto_increment,
title TEXT NOT NULL,
content LONGTEXT NOT NULL
primary key(id)
Then, create some views to display the output in the format you want, eg -
Create view graded_students as
(select s.student,
w.gradedesc as grade,
w.grade as points,
w.title
from work w,
student s
where w.student_id = s.id
and w.grade is not null )
Create view aggregate_grades as
(select s.student,
sum(w.grade) as "total grades",
from work w,
student s
where w.student_id = s.id
and w.grade is not null
group by s.student)
create view grades_by_assignment as
(select s.student,
a.title,
w.gradedesc as grade
from student s,
work w,
assignment a
where a.id = w.assignment_id
and s.id = w.student_id )
I'm more of an oracle and sql server person, so this may not be 100% mysql compliant (and I haven't tested it) but something like that would do the trick I think.
If you want to normalize the data even further you could break out the grade description/value to another table too, or just display the grade description in your application based on the stored value (eg grade value 4.5 always displays as "A", etc)