SQL Order By Count - mysql

If I have a table and data like this:
ID | Name | Group
1 Apple A
2 Boy A
3 Cat B
4 Dog C
5 Elep C
6 Fish C
and I wish to order it according to the total of Group from smallest to largest value, such as :
A - 2 records , B - 1 record , C - 3 records , so it will become:
3 Cat B
1 Apple A
2 Boy A
4 Dog C
5 Elep C
6 Fish C
I tried
$sql = "SELECT ID,Name FROM table ORDER BY COUNT(Group)";
but it just returns one result for me.
Are there any hints? Thank you.

You need to aggregate the data first, this can be done using the GROUP BY clause:
SELECT Group, COUNT(*)
FROM table
GROUP BY Group
ORDER BY COUNT(*) DESC
The DESC keyword allows you to show the highest count first, ORDER BY by default orders in ascending order which would show the lowest count first.

...none of the other answers seem to do what the asker asked.
For table named 'things' with column 'group':
SELECT
things.*, counter.count
FROM
things
LEFT JOIN (
SELECT
things.group, count(things.group) as count
FROM
things
GROUP BY
things.group
) counter ON counter.group = things.group
ORDER BY
counter.count ASC;
which gives:
id | name | group | count
---------------------------
3 | Cat | B | 1
1 | Apple | A | 2
2 | Boy | A | 2
4 | Dog | C | 3
5 | Elep | C | 3
6 | Fish | C | 3

SELECT group, COUNT(*) FROM table GROUP BY group ORDER BY group
or to order by the count
SELECT group, COUNT(*) AS count FROM table GROUP BY group ORDER BY count DESC

Try :
SELECT count(*),group FROM table GROUP BY group ORDER BY group
to order by count descending do
SELECT count(*),group FROM table GROUP BY group ORDER BY count(*) DESC
This will group the results by the group column returning the group and the count and will return the order in group order

SELECT * FROM table
group by `Group`
ORDER BY COUNT(Group)

Try using below Query:
SELECT
GROUP,
COUNT(*) AS Total_Count
FROM
TABLE
GROUP BY
GROUP
ORDER BY
Total_Count DESC

Below gives me opposite of what you have. (Notice Group column)
SELECT
*
FROM
myTable
GROUP BY
Group_value,
ID
ORDER BY
count(Group_value)
Let me know if this is fine with you...
I am trying to get what you want too...

Q. List the name of each show, and the number of different times it has been held.
List the show which has been held most often first.
event_id show_id event_name judge_id
0101 01 Dressage 01
0102 01 Jumping 02
0103 01 Led in 01
0201 02 Led in 02
0301 03 Led in 01
0401 04 Dressage 04
0501 05 Dressage 01
0502 05 Flag and Pole 02
Ans:
select event_name, count(show_id) as held_times from event
group by event_name
order by count(show_id) desc

Related

Group all rows after nth row together

I have the current table:
+----------+-------+
| salesman | sales |
+----------+-------+
| 1 | 142 |
| 2 | 120 |
| 3 | 176 |
| 4 | 140 |
| 5 | 113 |
| 6 | 137 |
| 7 | 152 |
+----------+-------+
I would like to make a query to retrieve the 3 top salesman, and an "Other" column, that would be the sum of everyone else. The expected output would be:
+----------+-------+
| salesman | sales |
+----------+-------+
| 3 | 176 |
| 7 | 152 |
| 1 | 142 |
| Others | 510 |
+----------+-------+
I am using MySQL, and I am experienced about it, but i can't imagine a way of doing this kind of GROUP BY.
A tried UNION with 2 SELECT, one for the top 3 salesman and another select for the "Others", but I couldn't figure a way of excluding the top 3 from the 2nd SELECT
You can do this by LEFT JOINing your table to a list of the top 3 salesmen, and then grouping on the COALESCEd salesman number from the top 3 table (which will be NULL if the salesman is not in the top 3).
SELECT COALESCE(top.sman, 'Others') AS saleman,
SUM(sales) AS sales
FROM test
LEFT JOIN (SELECT salesman AS sman
FROM test
ORDER BY sales DESC
LIMIT 3) top ON top.sman = test.salesman
GROUP BY saleman
ORDER BY saleman = 'Others', sales DESC
Output:
saleman sales
3 176
7 152
1 142
Others 510
Demo on dbfiddle
Using UNION, ORDER BY, LIMIT, OFFSET AND GROUP BY statements you should do the trick:
SELECT salesman, sales
FROM t
ORDER BY sales DESC LIMIT 3
UNION
SELECT 'Others', SUM(sales)
FROM (SELECT salesman, sales
FROM t
ORDER BY sales DESC LIMIT 3, 18446744073709551615) AS tt;
The big number at the end is the way to apply limit until the end of the table, as suggested here
This is a pain in MySQL:
(select salesman, count(*) as cnt
from t
group by salesman
order by count(*), salesman
limit 3
) union all
(select 'Others', count(*)
from t left join
(select salesman, count(*) as cnt
from t
group by salesman
order by count(*)
limit 3
) t3
on t3.salesman = t.salesman
where t3.salesman is null
);
This should be the fastest one if appropriate indexes are present:
(
SELECT salesman, sales
FROM t
ORDER BY sales DESC
LIMIT 3
)
UNION ALL
(
SELECT 'Other', SUM(sales) - (
SELECT SUM(sales)
FROM (
SELECT sales
FROM t
ORDER BY sales DESC
LIMIT 3
) AS top3
)
FROM t
)
ORDER BY CASE WHEN salesman = 'Other' THEN NULL ELSE sales END DESC
this will work:
select salesman,sales from tablename a where a.salesman in (3,7,1)
union all
select 'others' as others,sum(a.sales) as sum_of_others from tablename a where
a.salesman not in (3,7,1) group by others;
check https://www.db-fiddle.com/f/73GjFXL3KsZsYnN26g3rS2/0

MySQL how to select first row each group with count

I have a table like this (simplified version):
+------+-------+-----+--------------+-----+
| id | name | age | company.name | ...
+------+-------+-----+--------------------+
| 1 | Adam | 21 | Google | ...
| 3 | Peter | 20 | Apple | ...
| 2 | Bob | 20 | Microsoft | ...
| 9 | Alice | 18 | Google | ...
+------+-------+-----+--------------------+
I need groups data with counting rows by any one column. And I need to get first row in each group. User select which column will be used to group.
If user select column age to group then results:
+------+------------+-------+
| id | group_name | count |
+------+------------+-------+
| 9 | 18 | 1 |
+------+------------+-------+
| 2 | 20 | 2 |
+------+------------+-------+
| 1 | 21 | 1 |
+------+------------+-------+
Column to group may be numeric or string.
Currently I does it by this query:
SELECT id, group_name, users_name, count(id) as count FROM (
SELECT persons.id as id, company.type as group_name, users.name as users_name
FROM persons
LEFT JOIN company on company.id = persons.company_id
LEFT JOIN position on position.id=persons.position_id
...
LEFT JOIN source on source.id=persons.source_id
WHERE ...
ORDER BY if(company.type = '' or company.type is null,1,0) ASC,
company.type ASC, IF(persons.status = '' or persons.status is null,1,0) ASC,
persons.status ASC, persons.id
) t1 GROUP BY group_name
but with new version mysql this SQL stoped works I think that order is ignored in sub-select.
I know that similar topics was wroted, but proposed solutions not working with my query. I have to join many tables, add multiple conditions and use cascade order and then select first row from each group. I will be very happy if solution will be optimised for performace.
---- EDIT ----
Proposed solution:
SQL select only rows with max value on a column
which suggest to use MAX() and GROUP BY not working well. For two reason
If grouped column include string, then query return not first row, but last row in each group.
If my dataset has a cascade order, I can not use MAX in a few columns at the same time.
I created sqlfiddle which include exact example.
http://sqlfiddle.com/#!9/23225d/11/0
-- EXAMPLE 1 - Group by string
-- base query
SELECT persons.*, company.* FROM persons
LEFT JOIN company ON persons.company_id = company.id
ORDER BY company.name ASC, company.id ASC;
-- grouping query
SELECT MAX(persons.id) as id, company.name, count(persons.id) as count
FROM persons
LEFT JOIN company ON persons.company_id = company.id
GROUP BY company.name
ORDER BY company.name ASC, persons.id ASC;
-- The results will be:
-- |ID | NAME | COUNT|
-- |1 | Google | 2 |
-- |3 | Microsoft| 3 |
-- EXAMPLE 2 - Cascade order
-- base query
SELECT persons.*, company.* FROM persons
LEFT JOIN company ON persons.company_id = company.id
ORDER BY company.type ASC, persons.status ASC;
-- grouping query
SELECT MAX(persons.id) as id, company.type, count(persons.id) as count
FROM persons
LEFT JOIN company ON persons.company_id = company.id
GROUP BY company.type
ORDER BY company.type ASC, persons.status ASC;
-- The results will be:
-- |ID | NAME| COUNT|
-- |3 | 1 | 2 |
-- |2 | 2 | 3 |
Just change MAX() to MIN() to get the first row instead of the last row in each group.
To get the extreme values of cascading columns, see SQL : Using GROUP BY and MAX on multiple columns. Use that in the subquery part of the query to get the row containing those extremes, as in SQL select only rows with max value on a column.
So the form of the full query is:
SELECT t1.id, t1.grouped_column, t2.count
FROM yourTable AS t
JOIN (SELECT t3.grouped_column, t3.order_column1, MIN(t4.order_column2) AS order_column2, SUM(t3.count) AS count
FROM (SELECT grouped_column, MIN(order_column1) AS order_column1, COUNT(*) AS count
FROM yourTable
GROUP BY grouped_column) AS t3
JOIN yourTable AS t4
ON t3.grouped_column = t4.grouped_column AND t3.order_column1 = t4.order_column1
GROUP BY t4.grouped_column, t4.order_column1) AS t2
ON t1.grouped_column = t2.grouped_column AND t1.ordered_column1 = t2.order_column1 AND t1.order_column2 = t2.order_column2
Since you want to operate on a join, I suggest you define a view that uses the join. Then you can use that view in place of yourTable in the above query.

SQL Using Count() to find ids with frequencies other than MAX

I have a table called book_author with schema (book_id: int, author: string).
I am trying to obtain the books with the second highest number of authors.
That said, if the table was something like this:
book_id | author
_____________________
1 | John
2 | Anna
3 | Dan
1 | Robert
2 | Kim
1 | Oscar
3 | Bill
The return would be something like this. Book_id = 1 is not in the table because it has the max.
book_id | NUM_AUTHORS
2 | 2
3 | 2
My initial attempt was as follows, but this simply returns the book ids with the Maximum number of authors, not second to largest... Any way I could modify this?
SELECT book_id, COUNT(*) AS NUM_AUTHORS FROM book_author GROUP BY book_id
HAVING COUNT(*) =
(SELECT MAX(c) FROM
(SELECT COUNT(book_id) AS c
FROM book_author
GROUP BY book_id));
You can do this with ordering the counts in descending order and getting the 2nd row (with offset and limit)
select book_id,count(*)
from book_author
group by book_id
having count(*) = (select distinct cnt
from (select count(*) as cnt
from book_author
group by book_id
) t
order by cnt desc
limit 1 offset 1
)

mySQL rank order by sum of values in table

I have a mySQL database table I'm using to store messages sent out to people. Each message can contain points. I am wondering if there is a query that would sum up all the points per person and order them by rank?
Currently the only way I know how to get the output I'm looking for is to run this:
SELECT SUM(messagePoints) AS totalPoints FROM Messages
then store the result in PHP and order the result by totalPoints. I'm sure there is a way to do this entirely with a query but I don't have a clue where to start. Hoping somebody can give me some advice.
Messages Table example
messageID | personID | dateOfMessage | messagePoints
----------------------------------------------------------------
1 | 10 | 2017-01-05 00:00:00 | 5
2 | 10 | 2017-01-16 00:00:00 | 3
3 | 20 | 2017-01-16 00:00:00 | 4
4 | 10 | 2017-02-01 00:00:00 | 6
5 | 20 | 2017-02-07 00:00:00 | 7
Ideal Result:
personID | totalPoints | rank
--------------------------------
10 | 14 | 1
20 | 11 | 2
If the above output is possible, I'm wondering if it is possible to produce the same output with a date range filter such as only messages from the month of February.
Ideal Result only February messages:
personID | totalPoints | rank
--------------------------------
20 | 7 | 1
10 | 6 | 2
EDIT:
If it's easier, I ultimately need to display in my application the totalPoints and rank for 1 person (to be supplied by PHP) i.e. just results for personID = 10 I need both totalPoints and rank overall and filtered by date. So maybe 4 separate queries.
Ideal Overall Result for personID=10:
totalPoints
-----------
14
and
rank
----
1
Ideal Result for personID=10 Filtered by February:
totalPoints
-----------
6
and
rank
----
2
If you don't need the rank in the result - this is a basic aggregation query:
select m.personID, sum(messagePoints) as totalPoints
from messages m
group by m.personID
order by totalPoints desc
One way to include a row number (rank) to the result is to use a temporary table with an AUTO_INCREMENT column for the rank:
drop temporary table if exists tmp_messages;
create temporary table tmp_messages(
rank int auto_increment primary key,
personID int,
totalPoints int
) as
select null as rank, m.personID, sum(messagePoints) as totalPoints
from messages m
group by m.personID
order by totalPoints desc;
select * from tmp_messages order by rank;
You can ofcourse include fllters in the WHERE clause like:
select m.personID, sum(messagePoints) as totalPoints
from messages m
where m.dateOfMessage >= '2017-02-01'
and m.dateOfMessage < '2017-03-01'
group by m.personID
And you can select a specific user:
select * from tmp_messages where personID = 10
Demo: http://rextester.com/ASWF7717
You can try the below code to get the rank and you can use where clause to filter for dates.
select personID, SUM(messagePoints) AS totalPoints,#curRank := #curRank + 1 AS rank
from Messages, (select #curRank :=0) r
group by personID
order by totalPoints

Select AVG of a column and a single specific row

feedback table
-------------------------------
|rating|feedback|feedback_date|
-------------------------------
| 5 | good | 1452638788 |
| 1 | bad | 1452638900 |
| 0 | ugly | 1452750303 |
| 3 | ok | 1453903030 |
-------------------------------
desired result
average_rating | rating | feedback | feedback_date
2.25 | 3 | ok | 1453903030
Is it possible (in a single query) to select the average of one column and also one specific row from the table?
For example, i'd like to retrieve the average of the column rating and the most recent row as a whole.
I tried the following, and also with the ORDER BY direction as DSC but they both just gave me the average_rating and the first row in the table.
SELECT AVG(f.rating) AS average_rating, f.* FROM feedback f ORDER BY feedback_date ASC
SELECT * FROM feedback NATURAL JOIN (
SELECT AVG(rating), MAX(feedback_date) feedback_date FROM feedback
) t
See it on sqlfiddle.
you can do it with a sub query like this
SELECT AVG(f.rating) AS average_rating, t1.* FROM feedback f inner join (select * from feedback order by feedback_date asc limit 1 ) t1 on true
You can put a subquery in the SELECT clause, and calculate the average in the subquery.
SELECT (SELECT AVG(rating) FROM feedback) AS avg_rating, feedback.*
FROM feedback
ORDER BY feedback_date DESC
LIMIT 1