How to perform this MySQL query given this relation diagram - mysql

I have the following relation diagram where arrows represent Foreign Keys. The word in the blue is the table name and the words below are column names.
My question is how I could extract the following data from this table:
-what is the GPA of the student with ID=1?
-what are the average GPAs for students by department?
Given that: there are only
five letter grades with values A=4, B=3, C=2, D=1, and F=0, and GPA is the sum of
course credits x course grade value divided by total credits x 4. (so takes.grade is an int from 0-4 inclusive).
I have been trying to figure this out for hours with no avail. Could anyone steer me in the right direction?
Thanks for any help.

Ok, I've actually had to do this for a client over 15 yrs ago, and did for the entire database of all students, not as difficult once you have the pieces.
Without your exact queries as you want guidance.
Start with a single query that pulls data into a TEMPORARY table
CREATE TEMPORARY TABLE AllStudentsCourses
SELECT blah... from blah... join blah.. where... order by ...
A list of all classes a person has signed up for, and while you are at it, have columns computed at the PER-CLASS BASIS the grade earned A-F. You'll also need the credit hours of the class too as that is basis of computing a GPA.
a 3 hour course with an A gets 3 cr hrs towards GPA.
a 6 hour course with an A gets 6 cr hrs towards GPA.
a 6 hour course with a B gets LESS weighted value towards GPA
and you'll need to roll aggregates up.
Now, once you have all the classes a student attempted, you'll need to compute the per-class as sample described. If you want to apply that in the first query, do so.
Once you have that, then, you can roll-up the total credit hrs attempted vs credit hrs earned.
Basic math should help you along.

I didn't quite understand how the GPA is calculated, think there is something wrong with your original post. Here's a starting point that may or may not be right:
SELECT avg(t.grade)
FROM takes t
INNER JOIN course c
ON t.course_id = c.course_id
WHERE t.ID = 1;
SELECT c.dept_name, avg(grade)
FROM takes t
INNER JOIN course c
ON t.course_id = c.course_id
GROUP BY c.dept_name

Related

Assign rank automatically with its total score

I have managed my db in MySQL. My table looks like this:
.
There are a total of 6 types of ratings for each hospital. Each rating has max score of 10. Users vote each rating total of 10. I want to know how do I structure my query that it should automatically calculates rank of each hospital according to its total of rating values. Suppose hospital A has total (180) & hospital B has total (120) then hospital A given auto ranking as 1 & hospital B as 2. Since I have limited knowledge on sql I am not able to structure proper query.
Types of ratings are (charges, behaviour, admission, properInformation, hygine, treatment)
You will want to use a combination of GROUP BY and SUM.
Here are two good resources to help you with your problem.
MYSQL SUM GROUP BY
https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

MYSQL How to input externally calculated value into row

I'm having some issues with this MySQL query. I've got two tables, one that has a list of all the "Leaders of the Opposition"(People elected into office) with the date that they were elected. And I've got another table of all the people they've been married to, and the year they got married in.
I'm trying to make a query that returns all the Leaders of the Opposition ordered by their appointment date with their current spouses name at the time and the date of their marriage.
Here is some practice data of just one leader, dates changed a bit to fit the sort of problem I'm trying to solve.
TABLE ONE:
Leader_of_Opposition------Date Elected
Beazley K C, 1996-03-19
Beazley K C, 2005-01-28
TABLE TWO:
Leader_of_Opposition----Spouses's Name----Year Married
Beazley K C, Mary Ciccarelli, 1974
Beazley K C, Susie Annus, 2004
-
And I'm trying to get it to something like this:
Leader_of_Opposition------Date Elected------Spouses's name--------Year Married
Beazley K C, 1996-03-19, Mary Ciccarelli, 1974
Beazley K C, 2005-01-28, Susie Annus, 2004
-
So far I've got the basics of:
SELECT opposition.leader_name, opposition.time_begin, opposition_marriage.spouse_name, opposition_marriage.year_married'
FROM opposition, opposition_marriage
AND opposition.leader_name=opposition_marriage.leader_name
ORDER BY opposition.time_begin
But it gives me results where the leaders are mentioned multiple times for each marriage. And I can't figure out the syntax to search the other table then place that value into the row.
Any help would be extremely appreciated, been banging my head up against this one for a while now.
Thanks in advance.
I think this is going to be easiest with correlated subqueries. Alas, though, your tables do not have unique identifiers for each row.
SELECT o.leader_name, o.time_begin,
(select om.spouse_name
from opposition_marriage om
where o.leader_name = om.leader_name and om.year_married <= year(o.date_elected)
order by om.year_married desc
limit 1
) as spouse_name,
(select om.year_married
from opposition_marriage om
where o.leader_name = om.leader_name and om.year_married <= year(o.date_elected)
order by om.year_married desc
limit 1
) as year_married
FROM opposition o
ORDER BY o.time_begin;
This handles as many marriages as you like.
Now some comments:
It seems really strange to have a table only of marriages for the opposition leaders and not for all politicians.
The granularity is at the level of a "year", so a leader married in the same year after s/he takes office counts as being married to that spouse.
You do not have a "marriage end date", so a divorced or widowed partner would be considered current until the next marriage.
As I mention in the beginning, you should have a unique identifier for each row.

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.

MySQL: How to sum distinct rows in complex joined query

I have MySQL question I cannot solve myself (for the first time).
I have a query-with-parameters database plus PHP program that, together, generate extensive MySQL queries to run.
The problem is actually a simple one: that of correct summation. I need to SUM distinct rows (not values) within a complex, multi-joined query, and I cannot get it to work.
Do not ask why I work with the data structure below - I am working with data that is supplied to me and it needs to be this way. (The tables represent existing invoices.)
I will try to reproduce the situation very simplified here.
TABLE INVOICE
=============
Inv.Nr (ID) Other Data
------------------------
#1 Stuff
#2 Stuff
#3 More Stuff
TABLE INVOICE LINE
==================
ID Inv.Nr QUANTITY ArticleID UNIT PRICE
----------------------------------------------
1 #1 1 5 € 2.50
2 #1 1 109 € 4.00
3 #2 4 77 € 5.00
4 #2 10 91 € 6.00
TABLE INVOICE LINE VAT
======================
ID LINE-ID AMOUNT VATP VAT
1 1 € 2.00 25% € 0.50
2 2 € 2.00 25% € 0.50
3 2 € 1.42 6% € 0.08
4 3 €18.87 6% € 1.23
5 4 €16.00 25% € 4.00
6 4 €37.74 6% € 2.26
As you can see: some articles have a double VAT rate, because they consist of more elements that have different VAT rates (i.e. a book with a cd).
Now the queries are very long, there are much more tables joined that can have dynamic WHERE and GROUP BY clauses. So a query might look somewhat like (again much simplified):
SELECT `Inv.Nr`, ArticleID, SUM(Quantity), SUM(Amount), SUM(VAT)
FROM ((((`Invoice` INNER JOIN `Invoice Line`
ON `Invoice`.`Inv.Nr`=`Invoice Line`.`Inv.Nr`)
INNER JOIN `Invoice Line VAT`
ON `Invoice Line`.ID = `Invoice Line VAT`.`Line-ID`)
INNER JOIN `More Stuff`
ON .... )
INNER JOIN ....
ON ..... )
WHERE ....
GROUP BY .....
HAVING .....
The INNER JOINs defined by ... are many to 1, so Invoice Line VAT is on the many-side of both its JOIN relations.
The WHERE, GROUP BY and HAVING are semi-dynamically created in PHP code.
My problem is that i cannot get a proper SUM(Amount) and SUM(Quantity) at the same time, since the Quantity is added multiple times if there are multiple VAT rates to one invoice line.
SUM(DISTINCT Quantity) obviously doesn't work, since I need distinct rows, not values.
I cannot really create a subquery that either calculates the number of VAT rates (and divides the SUM(Quantity)), or calculates the Amount, since the subquery needs the same WHERE/HAVING parameters as the main query to work properly, and those are semi-dynamic (the queries are in a database and contain parameters that are filled in following the user's commands). Well, to be fair, I could do it, but it would leave the query-database and the php software extremely complicated, and I don't want to use a very complex solution for such a very simple problem, especially since someone else will have to maintain it in the future.
So how do I:
SUM the quantity only on distinct rows, or
COUNT the number of VAT rates per line, given the WHERE/HAVING (so without a subquery)?
I could add extra fields to the tables to help with this problem, but that possibility didn't help me - yet. For instance: storing the number of VAT rates doesn't help, since in the WHERE there may be a selection on VAT rate.
I hope it is something VERY simple that I overlooked, but I have been searching for hours now to no avail...
If anyone can help me that would be great! Thanks in advance!
EDIT: I found a solution, but I am not very pleased with it. I have to split up the WHERE, and SUM SUMs and repeat columns... It is UGLY and badly maintainable.
It is as follows:
SELECT `Inv.Nr`, ArticleID, SUM(Quantity), SUM(Amount), SUM(VAT)
FROM ((`Invoice` INNER JOIN `Invoice Line`
ON `Invoice`.`Inv.Nr`=`Invoice Line`.`Inv.Nr`)
INNER JOIN
(SELECT SUM(Amount) AS Amount, SUM(VAT) AS VAT, `Line-ID`
FROM ((`Invoice Line VAT`
INNER JOIN `More Stuff`
ON .... )
INNER JOIN ....
ON ..... )
WHERE some-where-stuff
GROUP BY `Line-ID`) x
ON `Invoice Line`.ID = x.`Line-ID`)
WHERE other-where-stuff
GROUP BY .....
HAVING .....
I hope someone got a more elegant, simpler solution!
In an update to the question, I answered the question myself. I said that I hoped for a less ugly and badly maintainable solution than:
SELECT `Inv.Nr`, ArticleID, SUM(Quantity), SUM(Amount), SUM(VAT)
FROM ((`Invoice` INNER JOIN `Invoice Line`
ON `Invoice`.`Inv.Nr`=`Invoice Line`.`Inv.Nr`)
INNER JOIN
(SELECT SUM(Amount) AS Amount, SUM(VAT) AS VAT, `Line-ID`
FROM ((`Invoice Line VAT`
INNER JOIN `More Stuff`
ON .... )
INNER JOIN ....
ON ..... )
WHERE some-where-stuff
GROUP BY `Line-ID`) x
ON `Invoice Line`.ID = x.`Line-ID`)
WHERE other-where-stuff
GROUP BY .....
HAVING .....
It turns out, that, now that I am working with this solution and rephrasing all my queries based in it, it is not so humongous and ugly after all. It turns out that it works quite well and much better than other solutions and workarounds I have tried. Because I guess there is no other solution than what I wrote I close this question by answering that above cited answer is the right one.
It turns out that using the correct SQL code instead of workarounds is the right way to do, even when it looks too complicated at first. And since there is nothing like SUM(DISTINCT ...) that works with distinct records instead of values, in this case the above code is the correct code.

MySQL Query to find an average based on some of the data in the table

I have a table which (amongst others) has fields dateawarded, beltcolor and users id (userid).
beltcolor is 'varchar' and dateawarded is 'date', userid is int.
This data represents the date that students were awarded their various color belts (Brazilian Jiu Jitsu).
What I would like to establish is the average time between belts / the average time spent at a given belt.
Since purple comes after blue, if I wanted to find out the average time spent on blue, I guess what I need to do is:
for each user id that has a purple belt (not all have made it to purple yet), take the date the the blue belt was awarded and the date the purple belt was awarded and calculate the time between them. Add all the times up and divide by the number of people who were found to have reached purple belt.
Can you help me with a SQL query?
Thanks!
Sean
This is a query to find out the average number of days between purple and blue. You can adapt it to other situations:
SELECT AVG(datediff(a.dateawarded,b.dateawarded)) as Average
FROM beltsTable a
INNER JOIN beltsTable b ON a.userid = b.userid
WHERE a.beltcolor = "purple" AND b.beltcolor = "blue"
sqlfiddle demo
This will only take in consideration the users that have both belts. If a user only has a blue belt, it will not be taken in consideration.
First you should find previous belt date for the current belt (see inner query) and then just count AVG of days between current date and previous for each belt color.
SQLFiddle demo
select beltcolor,
FLOOR(AVG(DATEDIFF(dateawarded,Prev_dateawarded))) as Days_to_reach
from
(
select beltcolor,dateawarded,id,
COALESCE((select MAX(dateawarded) from T
where id=T1.id
and dateawarded<T1.dateawarded)
,dateawarded)
as Prev_dateawarded
from T as T1
) As T2
group by beltcolor