MySql order by COUNT and list the total count - mysql

I am using this code to count and sort from my database:
$qry = "select entertainer, count(*) from final_results group by entertainer order by count(*) desc";
I get the right result, in as much as it lists all the contents in order or popularity.
What I would like is the top 5 results to display, with a count for each of them and then a total count of all the results.
e.g.
Top response (10)
Second response (8)
Third response (6)
etc...
Total count = 56
I would appreciate any help and advice.
Thanks,
John C

You can get the totaly using WITH ROLLUP, but that won't work well with the LIMIT 5 you want in order to only fetch the top five results. (Edit:) It will also not work with the ordering, as discussed in the comments.
So you'll either have to fetch all results, not just the top 5, and sort them in the application, or use two distinct queries, possibly merged on the server side using UNION the way #RedFilter suggests. But if you do two separate queries, the I personally would rather issue each one separately from the client application, as splitting the total from the top five later on is too much work for little gain.
To fetch all results, you'd use
select entertainer, count(*)
from final_results
group by entertainer with rollup
To do two distinct fetches you'd use
select entertainer, count(*)
from final_results
group by entertainer
order by count(*) desc
limit 5
and
select count(*)
from final_results
If you want both in a single union, you can do this as
(select 1 as unionPart, entertainer, count(*) as count
from final_results
group by entertainer
order by count(*) desc
limit 5)
union all
(select 2 as unionPart, 'Total count', count(*) as count
from final_results)
order by unionPart asc, count desc

select entertainer, count
from (
(select entertainer, count(*) as count, 1 as sort
from final_results
group by entertainer
order by count(*) desc
limit 5)
union all
(select 'Total count', (select count(*) from final_results), 2 as sort)
) a
order by sort, count desc

Related

Display count of column excluding min and max values

I want to count how many unique occurrences of an activity occurs in the table (FRIENDS) below. Then, I want to print the activities whom which their occurrences are not the maximum or minimum value of all occurrences.
***ID/Name/Activity***
1/James/Horse Riding
2/Eric/Eating
3/Sean/Eating
4/John/Horse Riding
5/Chris/Eating
6/Jessica/Paying
Ex:
Horse Riding occur 140 times
Playing occurs 170 times
Eating occurs 120 times
Walking occurs 150 times
Running occurs 200 times
The max occurrence here is Running, occurring 200 times, and the minimum occurrence here is Eating, occurring 120 times.
Therefore, I want to display
Horse Riding
Playing
Walking
In no particular order.
This is a code I have so far, but I keep getting a syntax error. When I don't get a syntax error, I get a "Every derived table must have its own alias error." I am new to SQL so I appreciate any advice I can get.
SELECT ACTIVITY, count(ACTIVITY) as Occurences FROM FRIENDS,
(SELECT MAX(Occur) AS Ma,MIN(Occur) AS Mi FROM (SELECT ACTIVITY, count(ACTIVITY) as Occur
FROM FRIENDS GROUP by City)) as T
GROUP BY City HAVING Occurences!=T.Ma AND Occurences!=T.Mi ORDER BY Occurences DESC
In MySQL 8.0, you can do this with aggregation and window functions:
select *
from (
select activity, count(*) cnt,
rank() over(order by count(*)) rn_asc,
rank() over(order by count(*) desc) rn_desc
from mytable
group by activity
) t
where rn_asc > 1 and rn_desc > 1
The subquery counts the occurences of each activity, and ranks them in both ascending and descending oders. All that is left to do is exclude the top and bottom records. If there are top ties (or bottoms), the query evicts them.
In earlier versions, an option is a having clause:
select activity, count(*) cnt
from mytable t
group by activty
having count(*) > (select count(*) from mytable group by activity order by count(*) limit 1)
and count(*) < (select count(*) from mytable group by activity order by count(*) desc limit 1)

Combine two SQL statements with arithmetic (not joining per se)

I have a plugin that counts helpful votes for reviews on my site ('helpful', 'funny', 'cool', and it gives the option of adding other descriptors as well. So I've added 'not helpful'.
Now I'm trying to customize the query used to display the reviews with the most votes by taking 'not helpful' votes into account (subtracting them from the helpful votes) instead of just counting a total of all votes.
So originally the query used for this was:
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` GROUP BY review_id ORDER BY count DESC LIMIT 5
And I've found that I can count the helpful votes (where the number in the 'rate' column is 0, 1, or 2), like so:
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` <3 GROUP BY review_id ORDER BY count DESC LIMIT 5
And I can count the non-helpful votes (where the number in the 'rate' column is 3), like so:
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` >2 GROUP BY review_id ORDER BY count DESC LIMIT 5
But what I can't figure out is how to combine these two select statements such that the top 5 'count' results show a difference of the counts from each query.
I don't want a union because no subtraction is done, and I've tried various permutations of multiple selects, but can't manage to work this out.
Any suggestions?
An example table: Example Table
In the example above, if all votes are counted blindly, review #4 is ranked higher than review #10, but if not-helpful votes are taken into account, #10 is ranked higher with a net total of 1 versus review #4's net total of 0.
Make sense?
try this:
select a.review_id,b.review_id,a.count,b.count from
(SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` <3 GROUP BY review_id ORDER BY count DESC LIMIT 5) as a
left join
(SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` >2 GROUP BY review_id ORDER BY count DESC LIMIT 5) as b
on a.review_id = b.review_id
this is work if review_id of first query = review_id of second query otherwise you can use also <> to both.
other option use union all
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` <3 GROUP BY review_id ORDER BY count DESC LIMIT 5
union all
SELECT review_id, COUNT(*) AS count FROM `wp_reviews_ratings` where `rate` >2 GROUP BY review_id ORDER BY count DESC LIMIT 5

Select sum of top three scores for each user

I am having trouble writing a query for the following problem. I have tried some existing queries but cannot get the results I need.
I have a results table like this:
userid score timestamp
1 50 5000
1 100 5000
1 400 5000
1 500 5000
2 100 5000
3 1000 4000
The expected output of the query is like this:
userid score
3 1000
1 1000
2 100
I want to select a top list where I have n best scores summed for each user and if there is a draw the user with the lowest timestamp is highest. I really tried to look at all old posts but could not find one that helped me.
Here is what I have tried:
SELECT sum(score) FROM (
SELECT score
FROM results
WHERE userid=1 ORDER BY score DESC LIMIT 3
) as subquery
This gives me the results for one user, but I would like to have one query that fetches all in order.
This is a pretty typical greatest-n-per-group problem. When I see those, I usually use a correlated subquery like this:
SELECT *
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3;
This is not the whole solution, as it only gives you the top three scores for each user in its own row. To get the total, you can use SUM() wrapped around that subquery like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId;
Here is an SQL Fiddle example.
EDIT
Regarding the ordering (which I forgot the first time through), you can just order by totalScore in descending order, and then by MIN(timestamp) in ascending order so that users with the lowest timestamp appears first in the list. Here is the updated query:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
and here is an updated Fiddle link.
EDIT 2
As JPW pointed out in the comments, this query will not work if the user has the same score for multiple questions. To settle this, you can add an additional condition inside the subquery to order the users three rows by timestamp as well, like this:
SELECT userId, SUM(score) AS totalScore
FROM(
SELECT userId, score, timeCol
FROM myTable m
WHERE(
SELECT COUNT(*)
FROM myTable mT
WHERE mT.userId = m.userId AND mT.score >= m.score
AND mT.timeCol <= m.timeCol) <= 3) tmp
GROUP BY userId
ORDER BY totalScore DESC, MIN(timeCol) ASC;
I am still working on a solution to find out how to handle the scenario where the userid, score, and timestamp are all the same. In that case, you will have to find another tiebreaker. Perhaps you have a primary key column, and you can choose to take a higher/lower primary key?
Query for selecting top three scores from table.
SELECT score FROM result
GROUP BY id
ORDER BY score DESC
LIMIT 3;
Can you please try this?
SELECT score FROM result GROUP BY id ORDER BY score DESC, timestamp ASC LIMIT 3;
if 2 users have same score then it will set order depends on time.
You can use a subquery
SELECT r.userid,
( SELECT sum(r2.score)
FROM results r2
WHERE r2.userid = r.userid
ORDER BY score DESC
LIMIT 3
) as sub
FROM result r
GROUP BY r.userid
ORDER BY sub desc
You should do it like this
SELECT SUM(score) as total, min(timestamp) as first, userid FROM scores
GROUP BY userid
ORDER BY total DESC, first ASC
This is way more efficient than sub queries. If you want to extract more fields than userid, then you need to add them to the group by.
This will of cause not limit the number of scores pr user, which indeed seems to require a subquery to solve.

SQL Join and sort

I have a MYSQL table of sports team results. Each event or match is stored with a for and against goals scored value. What I would like to do is retrieve an ordered list matches order by goals conceeded (ascending).
It seems simple enough until the team in question is the away team:
in which case we are looking at goals at for.
When the team in question is the home team we are looking at goals 'against'.
I can written the following query:
(SELECT * FROM `matches`,`teams`,`outcomes`
WHERE `home_team_id`=11 AND `matches`.away_team_id=`teams`.team_id
AND `matches`.score_id=`outcomes`.outcome_id
ORDER BY `against`,`date` DESC LIMIT 0,20)
UNION
(SELECT * FROM `matches`,`teams`,`outcomes`
WHERE `away_team_id`=11 AND `matches`.home_team_id=`teams`.team_id
AND `matches`.score_id=`outcomes`.outcome_id
ORDER BY `for`,`date` DESC LIMIT 0,20)
It works but the result set is in two halves, I want to combine the results and order by conceeded whether the team is home or away. Do I need an alias to do this?
Thank you.
Try to make a UNION query with the fields you need, renaming the for or against fields so that they have the same name. Then select everything from this table union and order by the renamed field:
select * from
((SELECT matches.*, teams.*, outcomes.against as goals
FROM matches,teams,outcomes
WHERE
matches.home_team_id=11
AND matches.away_team_id=teams.team_id
AND matches.score_id=outcomes.outcome_id
)
UNION
(SELECT matches.*, teams.*, outcomes.for as goals
FROM matches,teams,outcomes
WHERE matches.away_team_id=11
AND matches.home_team_id=teams.team_id
AND matches.score_id=outcomes.outcome_id
)) as union_table
order by goals, date desc limit 0,20;
This query executes perfectly in MySQL database.
since you already have the two halves you can just select everything from your query and sort accordingly:
SELECT * FROM
(
(SELECT * FROM `matches`,`teams`,`outcomes`
WHERE `home_team_id`=11 AND `matches`.away_team_id=`teams`.team_id
AND `matches`.score_id=`outcomes`.outcome_id
ORDER BY `against`,`date` DESC LIMIT 0,20)
UNION
(SELECT * FROM `matches`,`teams`,`outcomes`
WHERE `away_team_id`=11 AND `matches`.home_team_id=`teams`.team_id
AND `matches`.score_id=`outcomes`.outcome_id
ORDER BY `for`,`date` DESC LIMIT 0,20)
) as results order by results.conceeded asc

Finding the 2nd most expensive total products in MySQL

I'm working on simple queries to learn MySQL, in my example database, I keep track of Stores which sells electronic devices, I have a table Sells(Store, Item, Price).
And example data is,
'Best Buy', 'Galaxy S', 1000
'Buy More', 'Macbook Air', 2000
'Best Buy', 'Microsoft Mouse', 20
'Best Buy', 'Macbook Pro Cover', 40
'Buy More', 'Asus Zenbook', 2000
And so on..
I tried the following sql statement, but it says:
Error Code: 1111. Invalid use of group function 0.000 sec
SELECT store
FROM sells
WHERE SUM(price) <
(SELECT SUM(price) AS total
FROM sells
GROUP BY store
ORDER BY total DESC
LIMIT 1)
GROUP BY store
ORDER BY SUM(price) DESC
I would be appreciate if you can help me
Thanks
This will just plain show the second most expensive store;
SELECT STORE
FROM TABLE_A
GROUP BY STORE
ORDER BY SUM(PRICE) DESC
LIMIT 1,1
Demo here.
If you want the price displayed too, you can just select that too;
SELECT STORE, SUM(PRICE) TOTAL_PRICE
FROM TABLE_A
GROUP BY STORE
ORDER BY TOTAL_PRICE DESC
LIMIT 1,1
Demo here.
Edit: If you have several most expensive stores and several second most expensive stores, the query to get the all the second most expensive ones becomes quite a bit more convoluted; I'm sure someone can beat the efficiency of this one;
SELECT STORE, SUM(PRICE) TOTAL_PRICE
FROM TABLE_A
GROUP BY STORE
HAVING TOTAL_PRICE =
(SELECT SUM(PRICE) TMP
FROM TABLE_A
GROUP BY STORE
HAVING TMP <
(SELECT SUM(PRICE) TMP2
FROM TABLE_A
GROUP BY STORE
ORDER BY TMP2 DESC
LIMIT 1)
ORDER BY TMP DESC LIMIT 1)
Demo here.
You can do like this;
SELECT *,
SUM(price) AS totalprice
FROM sells
GROUP BY store
ORDER BY totalprice DESC
LIMIT 2
You first select the sum of the prices and store it temporarily in for ex. totalprice then as you already did group by store. To get the most expensive stores order the sum backwards and then limit to just two results.
You will be able to get the totalprice just as an ordinary column when you loop out the results
almost correct,
SELECT SUM(price) as price_total FROM sells GROUP BY store
if you want to order by you can do subquery, like:
SELECT price_total FROM (SELECT SUM(price) as price_total FROM sells GROUP BY store) as res ORDER BY price LIMIT 2
if you want to take 2nd you might make another query but i think it is better to use your back-end language
SELECT distinct price from sells ORDER BY price DESC, and in your code, just take the second one.
If you need the rest of the info, do this:
SELECT * from sells
WHERE price = (SELECT distinct price from sells ORDER BY price DESC LIMIT 1,1)
didn'T test it but should work
SELECT S.store
FROM (
SELECT SUM(T.price) AS sum_price
FROM formList_Total AS T
GROUP BY T.store
) AS S
ORDER BY sum_price DESC
LIMIT 1 , 1
Sorry, went to testing, here what i ended up with.