MySQL, sum up multiple rows and rank based on most votes - php

I have the following database
id rank1 rank2 rank3 rank4
1 5 4 8 9
2 5 8 9 4
3 8 5 3 1
4 5 8 2 1
5 8 5 3 1
6 5 8 3 1
i need a mysql query or php script that will tally up the ranks and display the top 4 based on the number of times it appears in the table... ie. the end result should look something like:
rank1 = 5
rank2 = 8
rank3 = 3
rank4 = 1
any ideas??? thanks in advance

Your table design is far from optimal, if you didn't think it was before you will definitely see it after realizing that the way to get the result you are after requires this "not that pretty" query, though it works.
SELECT name, rank FROM (
(
SELECT 'rank1' name, rank1 rank
FROM foobar GROUP BY rank1
ORDER BY count(*) DESC LIMIT 1
) rank1_foobar
)
UNION SELECT name, rank FROM (
(
SELECT 'rank2' name, rank2 rank
FROM foobar GROUP BY rank2
ORDER BY count(*) DESC LIMIT 1
) rank2_foobar
)
UNION SELECT name, rank FROM (
(
SELECT 'rank3' name, rank3 rank
FROM foobar GROUP BY rank3
ORDER BY count(*) DESC LIMIT 1
) rank3_foobar
)
UNION SELECT name, rank FROM (
(
SELECT 'rank4' name, rank4 rank
FROM foobar GROUP BY rank4
ORDER BY count(*) DESC LIMIT 1
) rank4_foobar
)
output
+-------+------+
| name | rank |
+-------+------+
| rank1 | 5 |
| rank2 | 8 |
| rank3 | 3 |
| rank4 | 1 |
+-------+------+
I would restructure your table into something as the below, that'd make it much easier to write queries as the one you've requested.
CREATE TABLE ranks (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
group_id INT UNSIGNED NOT NULL COMMENT 'to be able to group more than one row in `ranks` together',
rank_type ENUM('rank1','rank2','rank3','rank4'),
rank_value INT,
PRIMARY KEY(`id`)
);

Because of the poor data normalization, its not quite as simple as a single select/from group by. You need to query each "Rank" column as part of a union, THEN roll that up. To keep the interim temp summations down, we can still pre-group the counts so you are not running ALL rows 4 times, but the pre-roll-ups 1 per rank in the respective group segment
select
PreAgg.Rank,
SUM( PreAgg.RankCount ) as TotalCount
from
( select
YT.Rank1 as Rank,
COUNT(*) as RankCount
from
YourTable YT
group by
YT.Rank1
UNION ALL
select
YT.Rank2 as Rank,
COUNT(*) as RankCount
from
YourTable YT
group by
YT.Rank2
UNION ALL
select
YT.Rank3 as Rank,
COUNT(*) as RankCount
from
YourTable YT
group by
YT.Rank3
UNION ALL
select
YT.Rank4 as Rank,
COUNT(*) as RankCount
from
YourTable YT
group by
YT.Rank4 ) PreAgg
GROUP BY
PreAgg.Rank,
SUM( PreAgg.RankCount ) DESC
As pointed out by Ajreal, and it would need more clarification to the structure... Is there a reason why you have 4 distinct columns that are all "Rank" instead of a more normalized table something like..
ID RankGroup Rank
1 1 5
2 1 5
3 1 8
4 1 5
5 1 8
6 1 5
7 2 4
7 2 8
7 2 5
7 2 8
7 2 5
7 2 8
etc for ranks 3 and 4
Then you could just get your counts per RANK regardless of the "group level" condition, or get best ranking per group in very simplified query.

Related

postgreSQL ranking query with the given user_id

I am trying to get rank of a user by their two dimension params: donation sum and total donor count.
My rank formula is: rank of [rank of donation_sum + rank of donor_count / 2]
Sample table:
donation_id | user_id | donor_id | donation_sum
-----------------------------------------------
1 | 1 | 1 | 10
2 | 1 | 2 | 5
3 | 2 | 3 | 10
4 | 3 | 1 | 50
...
As you see, some donors make donation to different users, so I used sum(donation_sum) and count(distinct(donation_id)) to get exact rankings
I am able to get list of ranking separately by donation sum and total donor count with 2 sql but my need is to get a user rank with that formula above by given user_id in postgreSQL v. 9.4
Do you have any solution for it? so I will use that sql query in a Yii2 PHP framework
Thanks
Edit:
We added donation_date to the tbl_donation and modified actual query as below:
is it true usage of where donation_date?
with list as (
select
s.runner_id, sum, count, rank_sum, rank_count,
(rank_sum+ rank_count)::float/ 2 as rank_avg,
row_number() over (order by rank_sum) as rank
from (
select *, rank() over (order by sum desc) rank_sum
from (
select runner_id, sum(donation_sum)
from tbl_donation
where donation_date >= '2015-01-01'
group by 1
) s
) s
join (
select *, rank() over (order by count desc) rank_count
from (
select runner_id, count(distinct(donator_id))
from tbl_donation
where donation_date >= '2015-01-01'
group by 1
) c
) c
using (runner_id)
)
select rank
from list
where runner_id = 251;
Make two rankings in separate subqueries:
select
s.user_id, sum, count, rank_sum, rank_count,
(rank_sum+ rank_count)::float/ 2 as rank_avg,
row_number() over (order by rank_sum) as rank
from (
select *, rank() over (order by sum desc) rank_sum
from (
select user_id, sum(donation_sum)
from donations
group by 1
) s
) s
join (
select *, rank() over (order by count desc) rank_count
from (
select user_id, count(distinct(donation_id))
from donations
group by 1
) c
) c
using (user_id);
user_id | sum | count | rank_sum | rank_count | rank_avg | rank
---------+-----+-------+----------+------------+----------+------
3 | 100 | 1 | 1 | 2 | 1.5 | 1
1 | 30 | 2 | 2 | 1 | 1.5 | 2
2 | 20 | 1 | 3 | 2 | 2.5 | 3
(3 rows)
If you want to select rank for a single user_id use with query, e.g.:
with list as (
-- place here the above query
)
select rank
from list
where user_id = 2;

How to get maximum and average of a field from mysql table with some conditions

I have a table with records like following.
id score student_id course_id
---- ------ ---------- ----------
1 12 1 1
2 10 2 1
3 20 3 1
4 35 1 2
5 50 5 1
6 70 1 3
7 25 3 2
8 30 2 2
9 25 3 3
Currently i am running this query first
SELECT id,
score,
student_id,
course_id
FROM scores
WHERE student_id=1
The output of the query is
id student_id course_id score
-- ---------- --------- -----
1 1 1 12
4 1 2 35
6 1 3 70
THEN running LOOP ON RESULT AND finding avg_score, max_score USING this query
SELECT AVG(score) AS avg_score,
MAX(score) AS max_score
FROM scores WHERE course_id = RESULT['course_id']
and then adding them in the result. So finally i get the result with avg_score and max_score like this
id student_id course_id score avg_score max_score
-- ---------- --------- ----- --------- ---------
1 1 1 12 23 50
4 1 2 35 30 35
6 1 3 70 47.50 70
I want to know if it is possible to get the result using a single query instead of as i am doing right now.
I have tried this query but i get only 1 row instead of 3. Also the avg_score and max_score are wrong.
SELECT Score.id, Score.student_id, Score.course_id, Score.score, ROUND(AVG(mScore.score),2) as avg_score, MAX(mScore.score) as max_score
FROM `scores` AS `Score`
LEFT JOIN `scores` as `mScore` ON mScore.course_id = Score.course_id
WHERE Score.student_id = 1
Output of the above query is
id student_id course_id score avg_score max_score
-- ---------- --------- ----- --------- ---------
1 1 1 12 30.78 70
sqlfiddle link is http://sqlfiddle.com/#!2/8a3f1f/8
If it is not achievable using single query but there is better alternative than what i am doing now, please let me know.
PS: Here is the jsfiddle link of actual problem.
http://sqlfiddle.com/#!2/c50aa9/1/0
It has two tables. I simplified and combined two table into one in the question.
The single query i was trying gives only one row where it should give 4 rows like this query without avg_score and max_score http://sqlfiddle.com/#!2/c50aa9/3/0
SELECT id, score, student_id, course_id,
AVG(score) as avg_score,
MAX(score) as max_score
FROM scores
WHERE student_id = 1
group by id, score, student_id, course_id
And if you remove the where condition then you can get this for all students at once.
Following query solved the puzzle.
SELECT Score.id, Score.student_id, Score.course_id, Score.score, ROUND(AVG(mScore.score),2) as avg_score, MAX(mScore.score) as max_score
FROM `scores` AS `Score`
LEFT JOIN `scores` as `mScore` ON mScore.course_id = Score.course_id
WHERE Score.student_id = 1 GROUP BY Score.id
http://sqlfiddle.com/#!2/8a3f1f/10
If still there is any better solution, please let me know.

Symfony2 Finding Duplicate Rows and Summing Another Column

I am using Symfony2 and doctrine to grab all the duplicate rows and how many times they appear. That part I have down. Now, each of these rows has a qty field as well. What I am wanting to do is run my query and show how many times the duplicate row appears as well as sum the qty field for each of these rows:
My database table looks like this:
id | sku | qty
----------------------
1 A 1
2 B 1
3 A 3
4 A 5
5 A 1
6 A 1
7 B 2
8 A 1
9 A 1
Here is my initial query that grabs all duplicate rows:
SELECT o, count(o.id) as cnt
FROM WIC\APIBundle\Entity\FBAOrderHistory o
GROUP BY o.sku HAVING cnt > 1 order by cnt desc
It outputs this:
id | sku | cnt
----------------------
1 A 7
2 B 2
But what I want is this:
id | sku | cnt | qty
----------------------
1 A 7 13
2 B 2 3
Does anyone know how to do this. Thanks so much in advance for your help!
Use SUM()
SELECT o, count(o.id) as cnt ,sum(o.qty) as
FROM WIC\APIBundle\Entity\FBAOrderHistory o
GROUP BY o.sku HAVING cnt > 1 order by cnt desc
Using DQL
Mysql Demo

Count occurrences of distinct values in 2 fields

I am trying to find a MySQL query that will find distinct values in a particular field, count the number of occurrences of that value in 2 fields (1_user, 2_user) and then order the results by the count.
example db
+------+-----------+-----------+
| id | 1_user | 2_user |
+------+-----------+-----------+
| 1 | 2 | 1 |
| 2 | 3 | 2 |
| 3 | 8 | 7 |
| 4 | 1 | 8 |
| 5 | 2 | 8 |
| 6 | 3 | 8 |
+------+-----------+-----------+
expected result
user count
----- -----
8 4
2 3
3 2
1 2
The Query
SELECT user, count(*) AS count
FROM
(
SELECT 1_user AS USER FROM test
UNION ALL
SELECT 2_user FROM test
) AS all_users
GROUP BY user
ORDER BY count DESC
Explanation
List all the users in the first column.
SELECT 1_user AS USER FROM test
Combine them with the users from the second column.
UNION ALL
SELECT 2_user FROM test
The trick here is the UNION ALL which preserves duplicate values.
The rest is easy -- select the results you want from the subquery:
SELECT user, count(*) AS count
aggregate by user:
GROUP BY user
and prescribe the order:
ORDER BY count DESC
SELECT u, count(u) AS cnt
FROM (
SELECT 1_user AS u FROM table
UNION ALL
SELECT 2_user AS u FROM table
) subquery
GROUP BY u
ORDER by cnt DESC
Take the 2 queries:
SELECT COUNT(*) FROM table GROUP BY 1_user
SELECT COUNT(*) FROM table GROUP BY 2_user
Now combine them:
SELECT user, SUM(count) FROM
((SELECT 1_user as user FROM table)
UNION ALL
(SELECT 2_user as user FROM table))
GROUP BY user, ORDER BY count DESC;
I think this what you are looking for since your expected result did not include 7
select usr, count(usr) cnt from
(
select user_1 usr from users
union all
select user_2 usr from users
) u
where u.usr in (select user_1 from users)
group by usr
order by count(u.usr) desc

SQL Statement, always list 3 entries of a sort

I have another Problem
I just can show you this on an example:
I got a Table called "myTable" with 7 entries
myTable | id | user_id |
1 1
2 1
3 1
4 1
5 2
6 2
7 2
8 2
9 2
10 2
Now i want to list just 3 entries of any user_id, but i cant find out myself how to.
Please help!
------- EDIT
My result have to look like this:
id=1 | user_id=1
id=2 | user_id=1
id=3 | user_id=1
id=5 | user_id=2
id=6 | user_id=2
id=7 | user_id=2
select * from table as t1
where (select count(*) from table as t2
where t1.user_id = t2.user_id and t2.id < t1.id) <3
SELECT *
FROM table
ORDER BY ...
LIMIT 3
The LIMIT clause takes two arguments: number of rows to return, and an offset if you want to display something other than the first X rows. Details here in the MySQL docs.
If you want only 3 rows, you can use limit :
select * from myTable limit 3;
SELECT * FROM table LIMIT 3
http://dev.mysql.com/doc/refman/5.0/en/select.html

Categories