select average from average - mysql

I have the following table:
rating
--------
| id | account_id | room | kitchen | bathroom |
-----------------------------------------------
| 1 | 1 | 5 | 5 | 5 |
| 2 | 1 | 2 | 4 | 1 |
| 3 | 1 | 5 | 2 | 1 |
-----------------------------------------------
People can rate the room, kitchen and bathroom (from 1-5).
Average rating for ID = 1: 5 (because 15/3 = 5)
Average rating for ID = 2: 2.3333 (because 7/3 = 2.33333)
Average rating for ID = 3: 2.6666 (because 8/3 = 2.66665)
First question
As you can see, the average rating for ID = 2 => 2.3333... and for ID = 3 => 2.6666. How can I make it floor() and ceil()? (when < .5 => floor, when > .5 => ceil), so that the avg rating for ID = 2 becomes 2 (instead of 2.3333) and the avg rating for ID = 3 becomes 3 (instead of 2.6666...)
Second question
I want to select the average rating of the average ratings (so the average rating from all the rows together). So - when floor() and ceil() are used I have 3 average ratings: 5, 2 and 3 => 10/15 => 3. How do I get to the 3?
Thanks in advance!

For the first question, the answer is round():
select round( (room + kitchen + bathroom) / 3)
For the second, you would just use aggregation:
select avg(room + kitchen + bathroom)
from ratings;
If you want the average of the rounded results:
select round(avg(round(room + kitchen + bathroom)))
from ratings;
However, that seems strange to me.

Use ROUND function.
Query
SELECT id, ROUND(((room + kitchen + bathroom)/3), 0) as `average`
FROM rating
GROUP BY id;

Related

MySQL get weighted result

I have a Table with the following structure.
The Table has mostly records where gender = 1.
I'm looking for a solution to get a result set where on top around 60% of records have gender = 1 and around 40% with gender = 2 mixed, ordered by popularity desc.
The amount of member with gender = 2 is much less, which means after the result set should only have gender = 1 records.
Member table
id | nickname | gender | popularity
1 | jake | 1 | 80
2 | mike | 1 | 88
3 | dave | 1 | 75
4 | jenny | 2 | 85
5 | peter | 1 | 83
6 | nina | 2 | 88
7 | mister | 1 | 77
8 | drake | 1 | 80
Result should be something like, it must not meet exactly weighted list. the goal is to see mixed results of both genders.
id | nickname | gender | popularity
2 | mike | 1 | 88
5 | peter | 1 | 83
6 | nina | 2 | 88
1 | jake | 1 | 80
8 | drake | 1 | 80
4 | jenny | 2 | 85
7 | mister | 1 | 77
3 | dave | 1 | 75
My so far best result was (it don't take care about the 40:60 split):
SET #rank=0;
SET #rank2=0;
SELECT * FROM (
SELECT #rank:=#rank+1 AS rank, q.* FROM (SELECT * FROM test WHERE gender = 1 ORDER BY popularity DESC) AS q
UNION
SELECT #rank2:=#rank2+1 AS rank, q.* FROM (SELECT * FROM test WHERE gender = 2 ORDER BY popularity DESC) AS q
) AS r ORDER BY rank;
Please try...
SET #gender1Count = SELECT COUNT( * )
FROM tblMember
WHERE gender = 1;
SET #gender2Count = SELECT COUNT( * )
FROM tblMember
WHERE gender = 2;
SET #totalCount = SELECT COUNT( * )
FROM tblMember;
SELECT id AS id,
nickname AS nickname,
gender AS gender,
popularity AS popularity
FROM tblMember
JOIN ( SELECT id AS id
FROM tblMember
WHERE gender = 1
ORDER BY popularity DESC
LIMIT CASE
WHEN #gender1Count > #totalCount * 3 / 5
ROUND( #gender2Count * 3 / 2 )
ELSE
#gender1Count
END
UNION
SELECT id AS id
FROM tblMember
WHERE gender = 2
ORDER BY popularity DESC
LIMIT CASE
WHEN #gender1Count > #totalCount * 3 / 5
#gender2Count
ELSE
ROUND( #gender1Count * 2 / 3 )
END
) nominees ON tblMember.id = nominees.id
ORDER BY popularity DESC;
The above will give you a list where 60% of entries are gender = 1 and 40% are gender = 2. Please note that this is not the same as 60% or more of the total list as gender = 1 with the balance gender = 2 (or 40% or more of the total list as gender = 2 and the balance gender = 1).
It does this by forming a list of those whose gender equals 1 and sorting it into descending order of popularity. It then determines how many of the top entries it will grab from this list using LIMIT by checking if the count of gender = 1 members exceeds 60% (3/5ths) of the list. If it does then we will need to reduce the number of gender = 1 records to be retrieved to 3/2 times the count of gender = 2 members. The id's of the chosen records are then returned.
(A quickish explanation for those who aren't great at fractions, 40% is the same as 2/5 (two fifths). If gender = 2 has two fifths of the final list then gender = 1 must have the other three fifths (3/5). To find the size of 3/5ths of the list we start with the known 2/5ths (the count of gender = 2) and divide that into 2 so that we know the size of 1/5th of the list. We can then multiply this 1/5 by 3 to determine how many record will make up 3/5ths (60%) of our list.)
Similar logic is used to form the list of gender = 2 members to be included in the final list.
(Please note that the records at the end of each list will likely have popularity values equal to those of the most popular excluded members whose gender corresponds to each list. In the absence of any subsorting in the formation of the two lists the selection of those that are or are not chosen will be arbitrary (and essentially semirandom).)
The two lists are then joined using the UNION operator in what is a simple type of vertical join. (Note : The more familiar INNER JOIN, LEFT JOIN, etc., are all types of horizontal joins).
An inner JOIN is then performed upon our list of amalgamated id's with our original table, giving us our 60% / 40% list. Finally, this list is sorted into descending order of popularity.
If you have any questions or comments, then please feel free to post a Comment accordingly.

retrieve value of maximum occurrence in a table

I am in a very complicated problem. Let me explain you first what I am doing right now:
I have a table name feedback in which I am storing grades against course id. The table looks like this:
+-------+-------+-------+-------+-----------+--------------
| id | cid | grade |g_point| workload | easiness
+-------+-------+-------+-------+-----------+--------------
| 1 | 10 | A+ | 1 | 5 | 4
| 2 | 10 | A+ | 1 | 2 | 4
| 3 | 10 | B | 3 | 3 | 3
| 4 | 11 | B+ | 2 | 2 | 3
| 5 | 11 | A+ | 1 | 5 | 4
| 6 | 12 | B | 3 | 3 | 3
| 7 | 11 | B+ | 2 | 7 | 8
| 8 | 11 | A+ | 1 | 1 | 2
g_point has just specific values for the grades, thus I can use these values to show the user courses sorted by grades.
Okay, now first my task is to print out the grade of each course. The grade can be calculated by the maximum occurrence against each course. For example from this table we can see the result of cid = 10 will be A+, because it is present two times there. This is simple. I have already implemented this query which I will write here in the end.
The main problem is when we talk about the course cid = 11 which has two different grades. Now in that situation client asks me to take the average of workload and easiness of both these courses and whichever course has the greater average should be shown. The average would be computed like this:
all workload values of the grade against course
+ all easiness values of the grade against course
/ 2
From this example cid = 11 has four entries,have equal number of grades against a course
B+ grade average
avgworkload(2 + 7)/2=x
avgeasiness(3 + 8)/2 = y
answer x+y/2 = 10
A+ grade average
avgworkload(5 + 1)/2=x
avgeasiness(4 + 2)/2 = y
answer x+y/2 = 3
so the grade should be B+.
This is the query which I am running to get the max occurrence grade
SELECT
f3.coursecodeID cid,
f3.grade_point p,
f3.grade g
FROM (
SELECT
coursecodeID,
MAX(mode_qty) mode_qty
FROM (
SELECT
coursecodeID,
COUNT(grade_point) mode_qty
FROM feedback
GROUP BY
coursecodeID, grade_point
) f1
GROUP BY coursecodeID
) f2
INNER JOIN (
SELECT
coursecodeID,
grade_point,
grade,
COUNT(grade_point) mode_qty
FROM feedback
GROUP BY
coursecodeID, grade_point
) f3
ON
f2.coursecodeID = f3.coursecodeID AND
f2.mode_qty = f3.mode_qty
GROUP BY f3.coursecodeID
ORDER BY f3.grade_point
Here is SQL Fiddle.
I added a table Courses with the list of all course IDs, to make the main idea of the query easier to see. Most likely you have it in the real database. If not, you can generate it on the fly from feedback by grouping by cid.
For each cid we need to find the grade. Group feedback by cid, grade to get a list of all grades for the cid. We need to pick only one grade for a cid, so we use LIMIT 1. To determine which grade to pick we order them. First, by occurrence - simple COUNT. Second, by the average score. Finally, if there are several grades than have same occurrence and same average score, then pick the grade with the smallest g_point. You can adjust the rules by tweaking the ORDER BY clause.
SELECT
courses.cid
,(
SELECT feedback.grade
FROM feedback
WHERE feedback.cid = courses.cid
GROUP BY
cid
,grade
ORDER BY
COUNT(*) DESC
,(AVG(workload) + AVG(easiness))/2 DESC
,g_point
LIMIT 1
) AS CourseGrade
FROM courses
ORDER BY courses.cid
result set
cid CourseGrade
10 A+
11 B+
12 B
UPDATE
MySQL doesn't have lateral joins, so one possible way to get the second column g_point is to repeat the correlated sub-query. SQL Fiddle
SELECT
courses.cid
,(
SELECT feedback.grade
FROM feedback
WHERE feedback.cid = courses.cid
GROUP BY
cid
,grade
ORDER BY
COUNT(*) DESC
,(AVG(workload) + AVG(easiness))/2 DESC
,g_point
LIMIT 1
) AS CourseGrade
,(
SELECT feedback.g_point
FROM feedback
WHERE feedback.cid = courses.cid
GROUP BY
cid
,grade
ORDER BY
COUNT(*) DESC
,(AVG(workload) + AVG(easiness))/2 DESC
,g_point
LIMIT 1
) AS CourseGPoint
FROM courses
ORDER BY CourseGPoint
result set
cid CourseGrade CourseGPoint
10 A+ 1
11 B+ 2
12 B 3
Update 2 Added average score into ORDER BY SQL Fiddle
SELECT
courses.cid
,(
SELECT feedback.grade
FROM feedback
WHERE feedback.cid = courses.cid
GROUP BY
cid
,grade
ORDER BY
COUNT(*) DESC
,(AVG(workload) + AVG(easiness))/2 DESC
,g_point
LIMIT 1
) AS CourseGrade
,(
SELECT feedback.g_point
FROM feedback
WHERE feedback.cid = courses.cid
GROUP BY
cid
,grade
ORDER BY
COUNT(*) DESC
,(AVG(workload) + AVG(easiness))/2 DESC
,g_point
LIMIT 1
) AS CourseGPoint
,(
SELECT (AVG(workload) + AVG(easiness))/2
FROM feedback
WHERE feedback.cid = courses.cid
GROUP BY
cid
,grade
ORDER BY
COUNT(*) DESC
,(AVG(workload) + AVG(easiness))/2 DESC
,g_point
LIMIT 1
) AS AvgScore
FROM courses
ORDER BY CourseGPoint, AvgScore DESC
result
cid CourseGrade CourseGPoint AvgScore
10 A+ 1 3.75
11 B+ 2 5
12 B 3 3
If I understood well you need an inner select to find the average, and a second outer select to find the maximum values of the average
select cid, grade, max(average)/2 from (
select cid, grade, avg(workload + easiness) as average
from feedback
group by cid, grade
) x group by cid, grade
This solution has been tested on your data usign sql fiddle at this link
If you change the previous query to
select cid, max(average)/2 from (
select cid, grade, avg(workload + easiness) as average
from feedback
group by cid, grade
) x group by cid
You will find the max average for each cid.
As mentioned in the comments you have to choose wich strategy use if you have more grades that meets the max average. For example if you have
+-------+-------+-------+-------+-----------+--------------
| id | cid | grade |g_point| workload | easiness
+-------+-------+-------+-------+-----------+--------------
| 1 | 10 | A+ | 1 | 5 | 4
| 2 | 10 | A+ | 1 | 2 | 4
| 3 | 10 | B | 3 | 3 | 3
| 4 | 11 | B+ | 2 | 2 | 3
| 5 | 11 | A+ | 1 | 5 | 4
| 9 | 11 | C | 1 | 3 | 6
You will have grades A+ and C soddisfing the maximum average 4.5

How can I get the average of the values in three columns?

I have this table named rating:
id | helpful | necesary | lenght
1 | 3 | 1 | 5
1 | 4 | 3 | 2
2 | 5 | 3 | 5
3 | 3 | 3 | 5
1 | 1 | 2 | 3
3 | 2 | 3 | 2
This table stores the rating a user gives to anarticle from 1 to 5....How can I query the top 5 id that has the highest rating having averaged the 3 colums helpful, necesary, length?
AVG gives me the average for a column and LIMIT well gives me the top 5, but I can't get a start on how to average three columns.
Maybe you want to do this:
SELECT id FROM (
SELECT id, (helpful + necesary + legnth) / 3 average
FROM rating ORDER BY average DESC
) t
LIMIT 5;
The simplest way is just to add the values from the columns and divide them by the number of columns.
SELECT (helpful + necesary + length) / 3 AS "avg" FROM rating ORDER BY avg DESC LIMIT 5;
You can sum the values manually, sort by that sum, and then divide the sum by the number of columns to get the average:
select id, helpful, necesary, lenght,
((helpful + necesary + lenght) / 3) as avg
from rating
order by (helpful + necesary + lenght) desc
limit 5

Order by after element

Is there a way to do a query that orders by a field after a certain element id. I am trying to implement pagination based on the last returned element, and want to be able to both order elements a property and return the next paged based on the last element of the previous page.
For example a user may ask for 25 elements after element with id = 10 sorted on cost.
Imagine you have:
id | name | price
1 | Fish | 5
2 | Burger | 2
3 | Veggies | 6
If we want to get after id=2 sorted by price it should return
2 | Burger | 2
1 | Fish | 5
3 | Veggies | 6
If we want to get after id=1 sorted by price it should return
1 | Fish | 5
3 | Veggies | 6
You can do:
SELECT *
FROM YOURTABLE
WHERE id >= 10
ORDER BY cost ASC
LIMIT 25;
EDIT:
According to your new information, you can do that with:
SELECT *
FROM YOURTABLE
WHERE price >= (SELECT price from Table1 WHERE id = 2)
ORDER BY price ASC
LIMIT 25;
sql fiddle demo

Averaging an average in mySQL

I have a table
car
id | person_id | mpg
------------------------
4 | 1 | 50
5 | 1 | 15
6 | 2 | 10
7 | 2 | 28
8 | 3 | 33
I need to get an average of each person's mpg and then an average for the group.
person 1 avg = (50 + 15) / 2 = 32.5
person 2 avg = (10 + 28) / 2 = 19
person 3 avg = 33
group average = 32.5 + 19 + 33 / 3 = 28.1
Is there a query that will do what I need?
SELECT person_id, AVG(mpg) from car group by person_id;
If you want to get an average for the group, you should probably do this:
SELECT AVG(mpg) from car;
Unless you really want to average the averages, which seems a bit dubious to me:
SELECT AVG(average) from (SELECT person_id, AVG(mpg) as average from car group by person_id);
you cannot solve this in 1 query, but you have to use 2 queries or 1 query en solve the overal average in your code
select person, avg(mpg) from cat group by person
SELECT person_id, AVG(mpg) AS mpg_avg FROM car GROUP BY person_id WITH ROLLUP
The WITH ROLLUP-modifier will add a line to the result set where persion_id is NULL and mpg_avg is the average over the whole result set (MySQL >= 4.1.1):
person_id | mpg
------------------
1 | 32.5
2 | 19.0
3 | 33.0
NULL | 27.2