Efficient MySQL Database Design - mysql

I'm working on a small program for my school and I have a question about the following DB design.
The admin will assign subjects to teachers via an online form.
An example of a subject would be something like:
Subject Field 1: (Grade 5 Math: Teaching 5/1, 5/2 and 5/3) [Text Field]
Subject Field 2: (Grade 6 Math: Teaching 6/2 and 6/6) [Text Field]
I have added 7 subjects fields per their request as there won't be anyone that will exceed 7 subjects.
Each Subject Field will have required things to complete such as lesson plans, course syllabus etc...
Each requirement will have the following requirements:
Language (Language 1, Language 2 or Both) [Drop Down]
Type (Printed, File or Both) [Drop Down]
Time (Semester 1, Semester 2 or both) [Drop Down]
So far I have come up with this DB design:
ID (Primary, Auto Incremented)
TID (Teacher ID)
Year
Subject1
Subject1Requirement1
Subject1Requirement2
Subject1Requirement3
TimeSent
TimeReviewed
Subject2
Subject2Requirement1
Subject2Requirement2
Subject2Requirement3
TimeSent
TimeReviewed
Subject3
Subject3Requirement1
Subject3Requirement2
Subject3Requirement3
TimeSent
TimeReviewed
Continued to Subject 7.
I feel that there is a more efficient way of doing it but just can't think of a better way.
Thanks.

If there's no relationship between teacher's subjects, you can design 3 tables like the following
Teachers TeacherSubjects SubjectRequirements
---------- --------------- --------------------
ID SubjectID ----> SubjectID
TID --\ SubjectName SubjectRequirement
Year \--> TID Language
TimeSent Type
TimeReviewed Time
In such design
Each teacher can have multiple subjects (not limited to 7 subjects)
Each teacher's subject have multiple requirements (not limited to 5 requrirements)
Sample data
INSERT INTO Teachers(ID, TID, Year) VALUES (1,'LiuYan', 2012);
INSERT INTO Teachers(ID, TID, Year) VALUES (2,'Emily', 2012);
INSERT INTO TeacherSubjects (SubjectID, SubjectName, TID) VALUES ('SubjectID_1', 'SubjectName1', 'LiuYan');
INSERT INTO TeacherSubjects (SubjectID, SubjectName, TID) VALUES ('SubjectID_2', 'SubjectName2', 'LiuYan');
-- ...
INSERT INTO TeacherSubjects (SubjectID, SubjectName, TID) VALUES ('SubjectID_N', 'SubjectNameN', 'LiuYan');
INSERT INTO TeacherSubjects (SubjectID, SubjectName, TID) VALUES ('SubjectID_N+1', 'SubjectName N+1', 'Emily');
INSERT INTO TeacherSubjects (SubjectID, SubjectName, TID) VALUES ('SubjectID_N+2', 'SubjectName N+2', 'Emily');
-- ...
INSERT INTO TeacherSubjects (SubjectID, SubjectName, TID) VALUES ('SubjectID_M', 'SubjectName M', 'Emily');
INSERT INTO SubjectRequirements (SubjectID, SubjectRequirement, Language, Type, Time) VALUES ('SubjectID_1', 'Curriculum', 'Language 1', 'Printed', 'Semester 1');
INSERT INTO SubjectRequirements (SubjectID, SubjectRequirement, Language, Type, Time) VALUES ('SubjectID_1', 'Course Syllabus', 'Language 2', 'File', 'Semester 2');
INSERT INTO SubjectRequirements (SubjectID, SubjectRequirement, Language, Type, Time) VALUES ('SubjectID_1', 'Learning Management', 'Both Language', 'Both Type', 'Both Semester');
--...
INSERT INTO SubjectRequirements (SubjectID, SubjectRequirement, Language, Type, Time) VALUES ('SubjectID_N+1', 'Curriculum', 'Language 2', 'Both Type', 'Semester 2');
--...

On of the key aspects of database is having relationships (links) between tables - so that if a table (row) needs to refer to something else that is different then use another table and link together with foreign keys - this table, in our case TeacherSubject is a junction table
To give you a brief look of how I'd implement your requirements I think we need three tables, and the SubjectsTeacher below is a linking table to join the two.
You define each subject and teacher and then add entries to the TeacherSubject table to associate teachers with subjects.
To create the database:
CREATE TABLE `Subject` (
`SID` INTEGER NOT NULL AUTO_INCREMENT ,
`Name` VARCHAR(100) NOT NULL ,
`Curriculum` INTEGER NOT NULL ,
`Syllabus` INTEGER NOT NULL ,
`LearnManagement` INTEGER NOT NULL ,
`Individual Analysis` INTEGER NOT NULL ,
PRIMARY KEY (`SID`)
);
CREATE TABLE `Teachers` (
`TID` INTEGER NOT NULL AUTO_INCREMENT ,
`Name` VARCHAR(100) NOT NULL ,
PRIMARY KEY (`TID`)
);
CREATE TABLE `TeacherSubject` (
`id` INTEGER NOT NULL AUTO_INCREMENT ,
`SID_Subject` INTEGER NOT NULL ,
`TID_Teachers` INTEGER NOT NULL ,
PRIMARY KEY (`id`)
);
Then add the foreign keys that tells the database how your fields are linked - this step is important as it will ensure that you maintain data integrity and cannot insert bad data into this data.
ALTER TABLE `TeacherSubject` ADD FOREIGN KEY (SID_Subject) REFERENCES `Subject` (`SID`);
ALTER TABLE `TeacherSubject` ADD FOREIGN KEY (TID_Teachers) REFERENCES `Teachers` (`TID`);
Then setup a few rows for test.
INSERT INTO `Subject` (`SID`, `Name`, `Curriculum`, `Syllabus`, `LearnManagement`, `Individual Analysis`) VALUES
(1, 'Subject1', 1, 2, 3, 4),
(2, 'Subject1', 1, 2, 3, 4),
(3, 'Subject2', 1, 2, 3, 4),
(4, 'Subject3', 1, 2, 3, 4),
(5, 'Subject4', 1, 2, 3, 4);
INSERT INTO `Teachers` (`Name`) VALUES ('Teacher 1');
INSERT INTO `Teachers` (`Name`) VALUES ('Teacher 2');
INSERT INTO `TeacherSubject` (`id`, `SID_Subject`, `TID_Teachers`) VALUES
(1, 1, 1),
(2, 2, 1),
(3, 3, 2),
(4, 4, 2),
(5, 1, 2);
And finally to find out which subject teacher#1 has:
select * from TeacherSubject
INNER JOIN Teachers on TID=TID_Teachers
WHERE TID_Teachers=1

Related

how to find date difference from two different table in mys

I have two tables
1)LEAD TABLE (which have 3 columns)
Lead_ID || Created_Date || Industry
2)ACCOUNTS TABLE (which have 4 columns)
Account_ID||Created_Date|| Revenue_Range|| Lead_ID
How would I get the average number of days between a lead created and an account created
Don't pay attention to mess in data, I just randomly populated it.
Query returns leadId and difference in days between lead.created_date and account.created_date.
Query:
create table Leads
(
leadId int not null,
created_date datetime,
industry varchar(10),
PRIMARY KEY (leadId)
);
create table Accounts
(
accountId int not null,
created_date datetime,
revenue_range varchar(10),
leadId int not null,
FOREIGN KEY (leadId) REFERENCES Leads(leadId)
);
insert into Leads
values
(1, '2020-01-01', 'a'),
(2, '2020-01-02', 'b'),
(3, '2020-01-03', 'c'),
(4, '2020-02-01', 'd'),
(5, '2020-03-01', 'e');
insert into Accounts
values
(1, '2020-01-03', '1k', 1),
(2, '2020-03-10', '2k', 5),
(3, '2020-02-03', '3k', 2);
select
-- l.leadId,
-- l.created_date as LeadCreatedDate,
-- a.created_date as AccountCreatedDate,
-- ABS is used because it returns with minus sign
AVG(ABS(DATEDIFF(l.created_date, a.created_date))) as AvgDifferenceInDaysBetweenCreation
from Leads as l
inner join Accounts as a
on l.leadId = a.leadId;
You can try it out at SQLize Online

MySQL query parameters in a table field

Good afternoon.
I have two tables:
1 - table1 - (id, address, who_lives) who_lives field - text, comma-separated list of identifiers
2 - table2 - (id, name_of_resident)
composition of table 1
12| US Oregon | 12,13
14| US Washington| 9,11
composition of table 2
9 |Petrov
11|Sidorov
12|Ivanov
13|Popov
How to make MySQL request, preferably via join, that the result was
US Oregon |Ivanov,Popov
US Washington|Petrov,Sidorov
If possible, replace the comma with the br tag: Ivanov,Popov -> Ivanov >br< Popov
SELECT table1.address, GROUP_CONCAT(table2.name_of_resident SEPARATOR '<br>')
FROM table1
JOIN table2 ON FIND_IN_SET(table2.id, table1.who_lives)
GROUP BY table1.address
DEMO
That is not how you design tables. Don't do comma separated listes. Do either a (foreign) key from table 2 to table 1, or a table with table1.id and table2.id for each relation.
Then writing the query will be easier.
create table state (
state_id int primary key,
address varchar(255)
);
create table resident (
resident_id int primary key,
name varchar(255)
);
create table who_lives (
state_id int,
resident_id int,
primary key (state_id, resident_id)
);
insert into state values (12, 'US Oregon');
insert into state values (14, 'US Washington');
insert into resident values (9, 'Petrov');
insert into resident values (11, 'Sidorov');
insert into resident values (12, 'Ivanov');
insert into resident values (13, 'Popov');
insert into who_lives values (12, 12);
insert into who_lives values (12, 13);
insert into who_lives values (14, 9);
insert into who_lives values (14, 11);
select address, group_concat(name)
from state
join who_lives using (state_id)
join resident using (resident_id)
group by state_id;
http://sqlfiddle.com/#!9/e7041f/2

REPLACE INTO only if both columns are the same

WHAT I WANT
I want to REPLACE INTO my table only if both column1 and column2 are the same. Otherwise it should INSERT INTO even if column1 or column2 is the same.
WHAT I TRIED
I made column1 and column2 PRIMARY KEYS.
My SQL statement...
REPLACE INTO mytable (column1, column2, column3) VALUES (?, ?, ?)
It REPLACES instead of INSERTS even if just one of column1 or column2 is the same.
I tried to search for the answer but I don't know the proper language to get the proper results.
EDIT
I set up the primary keys with..
ALTER TABLE PKweight DROP PRIMARY KEY, ADD PRIMARY KEY(id,season);
My table..
I replaced the REPLACE INTO with INSERT INTO and I got this error...
PHP Fatal error: Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1' for key 'id' in
which tells me it is failing on the one duplicate of column id.
EDIT 2
The PHP/MYSQL code..
foreach($teamO as $tm){
$stmt = "REPLACE INTO PKweight (id, season, name, toi, weight_toi, standing, rank) VALUES (?, ?, ?, ?, ?, ?, ?)";
$dbConnection->prepare($stmt)->execute(["$tm->id", "$tm->season", "$tm->name", "$tm->toi", "$tm->weight_toi", "$tm->standing", "$tm->rank"]);
}
EDIT 3
Besides the compound PRIMARY KEY on id and season I also had a UNIQUE INDEX on id as shown by the gray key in the phpmyadmin screenshot above. I removed it by..
ALTER TABLE PKweight DROP INDEX id
I tried doing the same way you have mentioned but it is replacing and inserting based on the constraint created (two columns constraint) Created constraint on ID and First Name.
First I created a table:
CREATE TABLE Sakila.ReplacePrac (
Studentid INT(6) ,
Firstname VARCHAR(20),
Lastname VARCHAR(30) ,
Gender CHAR(1),
Primary key(Studentid, Firstname)) ;
insert into ReplacePrac
select 1, 'Rob', 'Stark', 'M' union all
select 1, 'Ben', 'Stark', 'M' union all
select 2, 'Arya', 'Stark', 'F' union all
select 2, 'Tyrian', 'Lan', 'M' ;
Currently table has this data:
# Studentid, Firstname, Lastname, Gender
1, Ben, Stark, M
1, Rob, Stark, M
2, Arya, Stark, F
2, Tyrian, Lan, M
I added Replace into clause and I think it is replacing whenever ID and First name match and inserting whenever it does not match (meaning it is "AND" condition rather than "OR")
Replace into Replaceprac (StudentID, Firstname, Lastname, Gender)
Values('1', 'Jon', 'Snow', 'M'),
('1', 'Ben', 'Snow', 'M'),
('2', 'Sansa', 'Startk', 'F'),
('2', 'Tyrian', 'Ster', 'M')
It replaced whenever it got match between ID and first name and inserted when ever ID or first name did not match. If you see it replaced Ben Start to Ben Snow, Tyrian Lan to Tyrian Ster and inserted for Jon and Sansa even if ID were already existing.
# Studentid, Firstname, Lastname, Gender
1, Ben, Snow, M
1, Jon, Snow, M
1, Rob, Stark, M
2, Arya, Stark, F
2, Sansa, Startk, F
2, Tyrian, Ster, M

MySQL sum of a calculation error

I'm struggling with calculating the sum of another calculation in MySQL Workbench. I don't really know how to explain it in words, so I'll provide some data.
Here I have tables and data:
drop database if exists GDPR;
create database if not exists GDPR;
use GDPR;
drop table if exists Company;
create table Company
(
id_company int not null auto_increment,
name varchar (50),
primary key (id_company)
) auto_increment = 1;
drop table if exists GDPR_steps;
create table GDPR_steps
(
id_step int not null auto_increment,
id_company int,
name varchar (50),
primary key (id_step),
foreign key (id_company) references Company (id_company)
) auto_increment = 1;
drop table if exists compliance;
create table compliance
(
id_com int not null auto_increment,
id_step int,
initiative varchar (50),
status varchar (10),
primary key (id_com),
foreign key (id_step) references gdpr_steps (id_step)
) auto_increment = 1;
insert into company
values (null, 'Mango'), (null, 'Kiwi');
insert into gdpr_steps
values (null, 1, 'Awareness'), (null, 1, 'Information you hold'),
(null, 2, 'Awareness'), (null, 2, 'Information you hold');
insert into compliance
values (null, 1, 'E-mail all employees', '1'),
(null, 1, 'E-mail all applicants', '0'),
(null, 2, 'Delete some data', '1'),
(null, 3, 'Call stakeholders', '1'),
(null, 4, 'Review data', '0');
I have this query which calculates the ratio of completion for each step by each company based on the status of each of the initiatives belonging to a certain step.
select
company.name as 'Company',
gdpr_steps.name as 'ID Step',
(sum(compliance.status)/count(compliance.status)) * 100 as 'Ratio %'
from
compliance, company, gdpr_steps
where
gdpr_steps.id_step = compliance.id_step
and company.id_company = gdpr_steps.id_company
group by
compliance.id_step, company.id_company;
The query above returns this output:
Company ID Step Ratio %
-----------------------------------------
Mango Awareness 50
Mango Information you hold 100
Kiwi Awareness 100
Kiwi Information you hold 0
Now, when I want to calculate the ratio per company (eg. summing the ratio from step1 with the one from step2 and divide it by 2) I can't make it work. Which would be like
Company Overall ratio %
Mango (Awareness (50) + Information you hold (100)) / nr of steps (2 in our case)
Kiwi (Awareness (0) + Information you hold (100)) / nr of steps (2 in our case)
Which in our case would result in something like:
Name Overall ratio %
Mango 75
Kiwi 50
I have tried something like
select
company.name,
((sum(compliance.status)/count(compliance.status)) * 100)/ count(gdpr_steps.id_step)
from
compliance, company, gdpr_steps
where
gdpr_steps.id_step = compliance.id_step
and company.id_company = gdpr_steps.id_company
group by
company.id_company;
This one doesn't seem to work at all, as I receive totally different values than expected.
Could you please explain me what I got wrong?
Kind regards!
It seems like you're after the following...
SELECT company
, AVG(`ratio %`) n
FROM
(
-- your query here --
) x
GROUP
BY company;

sql query to joint a data table with different data from another

it is hard to explain and my english doesn't help, but there it is:
I have two data tables: "nodes" and "records".
In the nodes table I save data of questions and data of answers
nodes:
| id | name | type |
records:
| id | questionid | answerid |
I've tried albeit unsuccessfully, to make an sql query to gave me the data of all records but with the name of the answer and question intead of the id.
In short, what I need is: records.id, nodes.name (question) , nodes.name (answer)
SELECT
questions.name AS questionname
answers.name AS answername
FROM records
INNER JOIN nodes AS questions ON records.questionid=questions.id
INNER JOIN nodes AS answers ON records.answerid=answers.id
You can use this query:
SELECT q.name AS question, a.name AS answer
FROM records r
LEFT JOIN nodes q ON q.id = r.questionid
LEFT JOIN nodes a ON a.id = r.answerid
WHERE 1
For this query I build schema below:
CREATE TABLE nodes (
id int auto_increment primary key,
name varchar(30),
type int(1));
INSERT INTO nodes (name, type) VALUES
('Is it a question 01', 0),
('Is it a question 02', 0),
('This is an answer 01', 1),
('This is an answer 02', 1),
('This is an answer 03', 1);
CREATE TABLE records (
id int auto_increment primary key,
questionid int(11),
answerid int(11));
INSERT INTO records (questionid, answerid) VALUES
(1, 3), (1, 4), (1, 5), (2, 4), (2, 5);
SQL Fiddle: http://sqlfiddle.com/#!2/bfd340/1