I currently have 2 tables STUDENTS and SUBJECTS. I need to create another table for ENROLLMENTS, the thing is that in that table I need to know the minimal attendance, which is both dependent on student (if he has a job it will be lower) and on subject (each subject has a base minimal attendance). Should I create another table like MINIMAL_ATTENDACE which holds the base minimal attendance of each Subject ? If so, is it good to have the primary key in that table be also the primary key from SUBJECTS ?
For example I have :
--STUDENTS--
ID NAME HIRED
1 Paul 1
--SUBJECTS--
ID NAME NUMBER_OF_TESTS M_ATTENDANCE
1 Math 2 10
--ENROLLMENTS--
ID STUDID SUBID M_ATTENDANCE
1 1 1 7 (Because Student is hired)
You can use a several constraints on a single column. Apparently, there is a many-to-many relationship between STUDENT and SUBJECT. A possible approach for implementing this (with SQL) is an "intersection" or "bridge" table is. You can use a composite PK, and foreign key constraints like this:
Demo (dbfiddle)
-- parent tables (and test data)
create table students (id primary key, name, hired)
as
select 1, 'Paul', 1 from dual union all
select 2, 'Maggie', 1 from dual union all
select 3, 'Herbert', 0 from dual ;
create table subjects (id primary key, name, number_of_tests, attendance_standard, attendance_hired)
as
select 100, 'Maths', 2, 10, 7 from dual union all
select 200, 'Biology', 3, 12, 8 from dual union all
select 300, 'Physics' 2, 10, 8 from dual ;
-- intersection
create table enrollments(
studentid number references students( id )
, subjectid number references subjects( id )
, constraint enrollments_pk primary key( studentid, subjectid )
) ;
-- test data for the "intersection" table:
-- every student needs to complete every subject
insert into enrollments
select S.id, SUB.id
from students S cross join subjects SUB ;
In the example (above), we assume that {1} the "hired" attribute belongs to a STUDENT, and {2} the different "attendance" attributes are attributes of SUBJECT.
You can now just join the 3 tables, and find the required attendance for each student using the query below.
-- Get the attendance requirements for each student & their chosen subject
select
S.name, S.hired
, SUB.name
, case
when S.hired = 1 then SUB.attendance_hired
else SUB.attendance_standard
end as attendance_required
from students S
join enrollments E on S.id = E.studentid
join subjects SUB on SUB.id = E.subjectid
;
-- output
NAME HIRED NAME ATTENDANCE_REQUIRED
Paul 1 Maths 7
Maggie 1 Maths 7
Herbert 0 Maths 10
Paul 1 Biology 8
Maggie 1 Biology 8
Herbert 0 Biology 12
Paul 1 Physics 8
Maggie 1 Physics 8
Herbert 0 Physics 10
If the "hire" status of a STUDENT can change during an academic year (or semester), you'd probably need to add some more entities (and subsequently: tables) for keeping track of each STUDENT's hire state change.
If the problem is as simple as you describe, it would be sufficient if the subject has two minimal attendences, ie. one for students with, and one for students without jobs.
You can have a primary key also a foreign key, in order to enforce the rule that the second table does not have an element which is missing from the first table. However, mixing up foreign key with primary key this way is usually a bad idea, because your rule might change. It is customary to make a field of a composite primary key also a foreign key particularly in many-to-many tables, but unless you are worried of storage space, you might want to create a foreign key on a separate field, so you will have less worries if your rule changes.
However, what you intend to store can be computed with aggregation, you do not necessarily need a separate table for that. You just group by and select min. You might also create a view if that's better, or, if changes on the main table are frequent, then you might consider the creation of a materialized view.
Related
Here is the table descriptions:
Students
SID
SNAME
1
Adams
2
Jones
3
Smith
4
Baker
Teachers
TID
TNAME
60192
Howser
45869
Langley
Classes
CID
CNAME
IS318
Database
IS301
Java
Student info:
SID
CID
TID
Grade
1
IS318
60192
A
1
IS301
45869
B
2
IS318
60192
A
3
IS318
60192
B
4
IS301
45869
A
4
IS318
60192
A
SID and CID, Composite primary key. SID,CID -> Grade and SID,TID -> Grade.
Is the table still in 3NF
Not as far as I understand it:
A database relation (e.g. a database table) is said to meet third normal form standards if all the attributes (e.g. database columns) are functionally dependent on solely the primary key.
https://en.wikipedia.org/wiki/Third_normal_form
Since your attribute is not solely dependent on the primary key, then I think not.
Imagine in one data table, you had a person and their exam scores for different categories (math/science/english) and for different exam numbers (1, 2). Like so:
Person
Math
Science
Bob
88
76
Bob
90
99
Joe
48
46
Joe
70
69
Would it be better to normalize this table by expanding column-wise (so, we have a math1 and science1 column and a math2 and science 2 column, or just adding a row_id column?
Expanding the columns into math1, science1, math2, science2, etc. would not be "normalizing." It's an example of repeating groups, which is usually considered a violation of First Normal Form.
You said each of the multiple rows per person corresponds to a different exam. How would you know which exam each row goes with? Why not add a column exam_no?
What you are describing is a many-to-many (m:m) relationship, while dancing around implementing it. Your m:m is between entities Persons and Subject (categories), each of which becomes a table. You then resolve the m:m by creating a third table - call it exams.
create table persons ( person_id integer generated always as identity
, name text
-- other attributes strictly about each person
, constraint persons_pk
primary key (person_id)
) ;
create table subjects ( subject_id integer generated always as identity
, name text
, description text
-- other attributes strictly about each subject
, constraint subjects_pk
primary key (subject_id)
) ;
create table exams ( person_id integer
, subject_id integer
, exam_date date
, score integer
-- other attributes strictly about each exam
, constraint exams_pk
primary key (person_id, subject_id, exam_date)
, constraint exams2persons_fk
foreign key (person_id)
references persons(person_id)
, constraint exams2subject_fk
foreign key (subject_id)
references subjects(subject_id)
) ;
Note: the above is still a very simplified example but should provide guidance for resolving this type design issue. See fiddle for worked example.
I have 3 tables:
Shirts(model, color)
Gloves(model, color)
Socks(model, color)
Where model is primary key in all 3 tables
Now I need to make another table:
Products (model, price)
So, in products I need to get all models from my first 3 tables, in one single column. How can I do that?
In my opinion, you've designed it wrong. Suggestion (as a comment under the question, saying that products should be referenced by 3 another tables) is - again, in my opinion - wrong.
You shouldn't create separate tables for shirts, gloves or socks. What will you do when you start selling hats or trousers or shoes? Will you create new tables for all of those? Of course not - those are just clothes (products) types.
So - create one table that contains all types; when new type appears, simply add a new row into that table and reference it from the products table.
Something like this:
SQL> create table product_type
2 (id_type number primary key,
3 name varchar2(30)
4 );
Table created.
SQL> create table products
2 (id_product number primary key,
3 id_type number references product_type,
4 color varchar2(20),
5 price number
6 );
Table created.
SQL> insert into product_type
2 select 1, 'shirt' from dual union all
3 select 2, 'glove' from dual union all
4 select 3, 'socks' from dual;
3 rows created.
SQL> insert into products
2 -- shirt
3 select 100, 1, 'red', 100 from dual union all
4 -- gloves
5 select 101, 2, 'blue', 150 from dual;
2 rows created.
SQL>
Here come the shoes:
SQL> insert into product_type
2 select 4, 'shoes' from dual;
1 row created.
SQL> insert into products
2 select 113, 4, 'brown', 400 from dual;
1 row created.
SQL>
Conclusion: read about normalization.
If someone says that "colors should be in a separate table", well - perhaps, that wouldn't be a bad idea either. Furthermore, does the products table have to be expanded by a date column (which would show what price was valid at certain period)? Not a bad idea either. There are numerous options you can include into the model. I just tried to point you into the right direction.
A foreign key can be created from the Products table to each of the other tables. However, creating a foreign key from each of the other tables (Shirts, Gloves, Socks) is not possible (unless each Product exists in all three tables). A foreign key from Shirts, Gloves, and Socks would basically say: before an insert into the Products table ensure that there is a record in each of the three other tables with the same key.
As suggested by the other posters, it is probably better to consider a slightly different design (e.g. add a type column to the products table).
Also, by convention table names should be singular (Product, Shirt, Glove, Sock).
If you mean in an environment like phpMyAdmin, MySQLWorkbench etc. you can just manually go to each product table 'Shirt','Gloves','Socks' and add a reference/foreign key of the field 'Model' to the the field 'Model' in the table 'Product'. The way to do this depends entirely on the programm you are using. Note that ofcourse you should create the new Table either manually or by SQL code before you do this.
There is no problem for multiple tables to point to same other table via foreign keys.
If you want to write SQL code to this you can use something like this, which is the other way around (use three foreign keys in the table 'Product', one for each other table). Note that the tables Socks,Shirts and Gloves must have already been created.
DROP TABLE IF EXISTS `Product` ;
CREATE TABLE `Product` (
`Color` varchar(8),
`Model` varchar(14),
PRIMARY KEY(`Model`),
FOREIGN KEY('Model') REFERENCES `Socks`(`Model`) ON DELETE CASCADE,
FOREIGN KEY('Model') REFERENCES `Gloves`(`Model`) ON DELETE CASCADE,
FOREIGN KEY('Model') REFERENCES `Shirts`(`Model`) ON DELETE CASCADE
) ;
The way you want is something like this (done three times one for each table). Note that as i said before the table Product must have been already created.
DROP TABLE IF EXISTS `Socks` ;
CREATE TABLE `Socks` (
`Color` varchar(8),
`Model` varchar(14),
PRIMARY KEY(`Model`),
FOREIGN KEY('Model') REFERENCES `Product`(`Model`) ON DELETE CASCADE
);
My existing table structure is below:
Students table and columns are below:
Year
Student_Id
Subject_Type_Id
Quarter
Complete_DTTM
Column1
Existing data like below:
Students:
Year Student_Id Quarter Subject_Type_Id Complete_DTTM Column1
---------------------------------------------------------------------------------------
2006 1 1 1 Null x
2006 1 2 1 10/2/2006 xyz
2006 1 2 2 10/30/2006 abc
2006 1 2 3 11/20/2006 def
One student can take multiple subjects by saving each subject separately by picking from DropDownList.
Now new requirement is one student can take multiple subjects by selecting from Checkboxes and provide data for Column1 and Complete_DTTM and save.
The difference between old and new requirement is how user selecting subject types, in old from Drop down list, able to pick one subject only, Column1 and Complete_DTTM are different for each subject.
With the new requirement, they can pick multiple subjects from check boxes for a student and column1 and Complete_DTTM is same for all subjects.
NEW data going to be like below:
Year Student_Id Quarter Subject_Type_Id Complete_DTTM Column1
--------------------------------------------------------------------------
2015 1 1 1, 2, 3, 4 12/31/2015 abcdef
2015 1 2 1, 2, 3, 4, 5 1/1/2016 xyz
How do I change ‘Students’ table (I can add new table) to support multiple subject Ids and also needs to support old data (shown above for year 2006)?
Thanks in advance
You need to make a table that will house just the student data. Then you will need to make a table that will house Subject Type data. The a table to link them together.
CREATE TABLE Student (ID INT NOT NULL PRIMARY KEY, NAME VARCHAR(50) NOT NULL,n...)
Create Table for subject
CREATE TABLE Subject (ID INT NOT NULL PRIMARY KEY, Subject_Name VARCHAR(50)NOT NULL,n...)
Then You will need a table to link these two together.
CREATE TABLE Student_Suject_Link (Student_ID INT NOT NULL, SUBJECT_ID INT NOT NULL)
The link table will need to have a foreign key realtionship to the Student table and the Subject table. Student_ID needs to be a foreign key to the ID column in the student table and same for the subject table. The SUbject_ID needs to be a foreign key to the ID in the Subject table. This should satisfy 3rd normal form. And if you need to grab data it is easy enough to join tables to get what you need. I relaize it seems to create more work but it is infinately easier to manage that adding lists to tables. You can of course add other fields you deem necessary into the table it is just necessary to have the ID's to relate on. Hope this helps.
You now have a one to many relationship and you need a child table for the many part of the relationship. Do not ever store data in a comma delimited list. This is a way to totally break your performance when you want to find data int hose tables. It is just bad deign. You need to normalize those tables and I am sure that this is why you have been given this particular exercise.
I want to select result in sqlite from multiple tables with multiple foreign keys, I tried JOIN but it did not work that well; for example :
a table for STUDENT :
CREATE TABLE STUDENT (
STUDENT_NAME TEXT NOT NULL,
STUDENT_NUMBER INTEGER PRIMARY KEY NOT NULL,
STUDENT_ADDRESS TEXT NOT NULL
);
and a table for EXAMS :
CREATE TABLE EXAMS(
EXAM_CODE INTEGER PRIMARY KEY NOT NULL,
EXAM_SUBJECT TEXT NOT NULL,
EXAM_LOCATION TEXT NOT NULL
);
and a table called WROTE_EXAM to get the information for students who wrote a specific exam
CREATE TABLE WROTE_EXAM (
STUDENT_NUMBER INTEGER NOT NULL,
EXAM_CODE INTEGER NOT NULL,
DATE DATE NOT NULL,
FOREIGN KEY(STUDENT_NUMBER) REFERENCES STUDENT(STUDENT_NUMBER),
FOREIGN KEY(EXAM_CODE) REFERENCES EXAMS(EXAM_CODE));
this is a sample data inserted into tables :
STUDENT_NAME : John
STUDENT_NUMBER: 123456789
STUDENT_ADDRESS : 10th street
EXAM_CODE: 123
EXAM_SUBJECT: One Subject
EXAM_LOCATION: Class
now, I want to :
a) output student names, exam codes and student location, who wrote the exam
b) output exam code, exam subject and exam location for student with ID : 123456789
thanks
When joining tables you almost always want to include an explicit join condition. The SQLite syntax diagrams may be helpful:
So the SQL from your comment should look more like this:
select student.student_name, exams.exam_code, ...
from student
join wrote_exam using (student_number)
join exams using (exam_code)
where ...
or you could use JOIN...ON:
select student.student_name, exams.exam_code, ...
from student
join wrote_exam on student.student_number = wrote_exam.student_number
join exams on exams.exam_code = wrote_exam.exam_code
where ...
Then add where conditions to filter the results as needed.
Note that I've also added some table qualifiers to the columns in your SELECT clause, those are needed to uniquely specify which exam_code you're interested in and since one column needs to be qualified, I did them all for consistency; in real life I'd prefix them all to make things nice and explicit.
Also, I don't see a student_location anywhere, perhaps you mean student.student_address or exams.exam_location.