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;
Related
Goal
Retrieve InValid records but with some complexity. Let me explain.
Taking a look at this output: There is an InValid record for ItemId of 1.
Using the query below, I am able to see InValid records - to display to the user this Item needs to be Re-Checked.
SELECT * FROM `Records` WHERE IsValid = 0;
Problem
This is where I am stuck.
In the Red is the InValid record, recorded on the 28-06-2021. The next day, 29-09-2021 the ItemId is now a Valid record.
But using this query below is not relevant anymore, as it will show me the records that are still Invalid. Even though the record has been validated the next day.
SELECT * FROM `Records` WHERE IsValid = 0;
My idea to solve this problem (See Edit 1 below, for further details to this solution)
My idea would be to create a Trigger that will check if today's record is valid, if it is valid, then Update all the items to true where date is before today. From here, I can use the simple query above to see InValid records.
Also, I thought of creating a History table and a trigger to see what actions have been performed on the Records table.
Question
I am not sure if my idea is appropriate to solve my problem, creating a trigger to update all previous records does not seem the records are valid at all. But, my history table will show me valid values.
Is there a query I can use and avoid creating any triggers or it's best to go with my solution?
Schema (MySQL v5.7)
-- Base
CREATE TABLE `Items` (
Id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
ItemName VARCHAR(30));
CREATE TABLE `Records` (
Id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Date TIMESTAMP NOT NULL,
ItemId INT NOT NULL,
IsValid BOOLEAN NOT NULL,
FOREIGN KEY (ItemId) REFERENCES `Items`(Id));
-- History
CREATE TABLE `Records_History` LIKE `Records`;
ALTER TABLE `Records_History`
MODIFY COLUMN Id INT NOT NULL,
DROP PRIMARY KEY,
ADD Action VARCHAR(8) DEFAULT 'insert' FIRST;
CREATE TRIGGER Records_AfterInsert
AFTER INSERT ON `Records` FOR EACH ROW
INSERT INTO `Records_History` SELECT 'insert', d.*
FROM `Records` AS d WHERE d.Id = NEW.Id;
-- Insert Records
INSERT INTO `Items`
(ItemName)
VALUES
('Item 1'),
('Item 2');
INSERT INTO `Records`
(Date, ItemId, IsValid)
VALUES
('2021-09-28', 1, 0),
('2021-09-28', 2, 1),
('2021-09-29', 1, 1),
('2021-09-29', 2, 1);
Query #1
select * from `Records`;
Id
Date
ItemId
IsValid
1
2021-09-28 00:00:00
1
0
2
2021-09-28 00:00:00
2
1
3
2021-09-29 00:00:00
1
1
4
2021-09-29 00:00:00
2
1
Query #2
select * from `Records_History`;
Action
Id
Date
ItemId
IsValid
insert
1
2021-09-28 00:00:00
1
0
insert
2
2021-09-28 00:00:00
2
1
insert
3
2021-09-29 00:00:00
1
1
insert
4
2021-09-29 00:00:00
2
1
View on DB Fiddle
Edit 1
Unfortunately my solution is not an option. As I will hit this error:
Tried my solution: Error Code: 1442. Can't update table 'Records' in stored function/trigger because it is already used by statement which invoked this stored function
This basically means: I have a chance of causing an infinite loop.
This is what I have done to achieve my goal:
Schema (MySQL v5.7)
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''));
CREATE TABLE `Items` (
Id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
ItemName VARCHAR(30));
INSERT INTO `Items`
(ItemName)
VALUES
('Item 1'),
('Item 2');
CREATE TABLE `Records` (
Id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Date TIMESTAMP NOT NULL,
ItemId INT NOT NULL,
IsValid BOOLEAN NOT NULL,
FOREIGN KEY (ItemId) REFERENCES `Items`(Id));
INSERT INTO `Records`
(Date, ItemId, IsValid)
VALUES
('2021-09-28', 1, 1),
('2021-09-28', 2, 1),
('2021-09-29', 1, 0),
('2021-09-29', 2, 1),
('2021-09-30', 1, 1),
('2021-09-30', 2, 1),
('2021-10-01', 1, 0),
('2021-10-01', 2, 1);
Query #1
SELECT * FROM Records a WHERE IsValid = 0 AND ItemId NOT IN (
SELECT ItemId FROM Records b WHERE IsValid = 1 AND b.Date >= a.Date
) GROUP BY ItemId;
Id
Date
ItemId
IsValid
7
2021-10-01 00:00:00
1
0
View on DB Fiddle
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
i have one field wonum,C_criticality in workorder.
In C_criticality field have three option
critical
non-critical
tools
4.null or empty
In that C_criticality field some columns are null(empty) that should come at last.
Now i have to get the output in sorting order by criticality , noncritical ,tool , null value(empty value ) will come.
CREATE TABLE workorder
(
wonum int,
C_criticality varchar(255),
);
INSERT INTO workorder (wonum,C_criticality)
VALUES (2, 'critical');
INSERT INTO workorder (wonum,C_criticality)
VALUES (1, 'non-critical');
INSERT INTO workorder (wonum,C_criticality)
VALUES (15, 'critical');
INSERT INTO workorder (wonum,C_criticality)
VALUES (12, 'tool');
INSERT INTO workorder (wonum,C_criticality)
VALUES (21, 'non-critical');
INSERT INTO workorder (wonum,C_criticality)
VALUES (11, '');
output:-
C_criticality wonum
critical 2
critical 15
non-critical 21
tool 12
null 11
We can try a two-tiered sort using ISNULL and FIELD:
SELECT *
FROM yourTable
ORDER BY
ISNULL(C_criticality), -- 0 for non NULL, 1 for NULL
FIELD(C_criticality, 'criticality', 'noncritical', 'tool');
The call to FIELD will order according to the list you provided.
This works for all SQL engines, not just in MySQL
select *
from workorder
order by case when C_criticality = 'critical' then 1
when C_criticality = 'non-critical' then 2
when C_criticality = 'tools' then 3
else 4
end
Since you have tagged mysql the query would be something like ,
SELECT * FROM workorder ORDER BY FIELD(C_criticality ,'critical','non-critical','tools','null');
It depends from DBMS. In some there are options NULLS FIRST, NULLS LAST
In MySQL you can find this article interesting https://www.designcise.com/web/tutorial/how-to-order-null-values-first-or-last-in-mysql
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
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