Finding an element with max. no of appearing in SQL - mysql

I have the following table:
id | year
10 | 2000
11 | 2001
10 | 2002
12 | 2003
11 | 2004
13 | 2005
10 | 2006
10 | 2007
According to id, since 10 appears most, the selection should give 10 for this table. I know this is easy but I couldn't go further than COUNT(*).

The following SQL will work when there is more then one id having the maximum count:
SELECT id FROM table GROUP BY 1
HAVING COUNT(*)=( SELECT MAX(t.count)
FROM ( SELECT id,COUNT(*) AS count
FROM table GROUP BY 1 ) t )
The first (innermost) SELECT will just count each id, this is used in the second SELECT to determine the maximum count and this will be used in the final (outermost) SELECT to display only the right IDs.
Hope that helps.

You need a group by, order by - along with a limit:
SELECT id FROM sometable GROUP BY id ORDER BY COUNT(*) DESC LIMIT 1
This will group the table by id, order them in descending order by their count and pick the first row (the one with highest count).

Related

Group by with sum doesn't return correct result

Say a table has this schema :
grp | number
1 | 10
1 | 10
1 | 10
2 | 30
2 | 30
3 | 20
Note that each unique grp has a unique number even if there are more than 1 grp. I'm looking to sum all numbers for each unique grp.
So I want to group my table by grp to have this :
grp | number
1 | 10
2 | 30
3 | 20
And then get the sum which is now 60, but without grouping it gets me 110 as it calculates the sum of everything without grouping. All in one query, with no sub-queries if possible.
I've tried doing the following :
SELECT sum(number) as f
FROM ...
WHERE ...
GROUP BY grp
But this doesn't work, it returns multiple results and not the single result of the sum. What am I doing wrong?
You can use subquery to select unique records & do the sum:
select sum(number)
from (select distinct grp, number
from table t
) t;
If you group by the group, then you'll get one result for each group. And it won't take into account the fact that you only want to use the value from each group once.
To get your desired result, taking one row from each group, you first need to make a subquery selecting DISTINCT group/number combinations from the table, and then SUM that.
SELECT
sum(`number`) as f
FROM
(SELECT DISTINCT `grp`, `number` FROM table1) g
This will output 60.
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=8a3b346041731a4b4c85f4e151c10f70

mysql get last N records with MAX(date)

So I have following data in a product_rate_history table -
I want to select last N records ( eg 7 records ) informing rate change history of given product. If product rate is changed more than one time a day, then query should select most recent rate change for that day.
So from above table I want output like following for product id 16-
+-----------+-------------------------+------------------------+
| product_id | previous_rate | date |
+----------------+--------------------+------------------------|
| 16 | 2400 | 2016-04-30 23:05:35 |
| 16 | 4500 | 2016-04-29 11:02:42 |
+----------------+--------------------+------------------------+
I have tried following query but it returns only one row having last update rate only-
SELECT * FROM `product_rate_history` prh
INNER JOIN (SELECT max(created_on) as max FROM `product_rate_history` GROUP BY Date(created_on)) prh2
ON prh.created_on = prh2.max
WHERE prh.product_id = 16
GROUP BY DATE(prh.created_on)
ORDER BY prh.created_on DESC;
First, you do not need an aggregation in the outer query.
Second, you need to repeat the WHERE clause in the subquery (for the method you are using):
SELECT prh.*
FROM product_rate_history prh INNER JOIN
(SELECT max(created_on) as maxco
FROM product_rate_history
WHERE prh.product_id = 16
GROUP BY Date(created_on)
) prh2
ON prh.created_on = prh2.maxco
WHERE prh.product_id = 16
ORDER BY prh.created_on DESC;

Sort the SQL table by name using maximum of quantity

I have an SQL selection which return the following:
Name Code Qty
Janet 10 6
Janet 11 9
Janet 09 8
Jones 12 7
Jones 11 8
James 09 5
James 10 4
I want this selection to get sorted based on the qty for all the three people : order the people by their maximum quantity, and then order by quantity.
The output should look like this:
Janet 11 9
Janet 09 8
Janet 10 6
Jones 11 8
Jones 12 7
James 09 5
James 10 4
Any way to achieve this?
This is a subtle problem. It looks like you want to sort the names by the maximum of qty. This requires a join and aggregation to get the maximum qty for each name:
select t.*
from table t join
(select name, max(qty) as maxq
from table t
group by name
) tt
order by tt.maxq desc, tt.name, t.qty desc;
Try this:
SELECT * FROM `names` ORDER BY name ASC, qty DESC
SELECT Name, Code, Qty
FROM names AS main JOIN
(SELECT Name, MAX(Qty) AS max_qty
FROM names
GROUP BY Name) AS max_names USING (Name)
ORDER BY max_names.max_qty DESC, names.Qty DESC
The virtual table max_names contains the maximal Qty for each Name:
Janet 9
Jones 8
James 5
Then you join it to the original table and sort according to this max_qty.
If you want to sort according to the total quantity per name, just replace MAX with SUM:
SELECT Name, Code, Qty
FROM names AS main JOIN
(SELECT Name, SUM(Qty) AS sum_qty
FROM names
GROUP BY Name) AS sum_names USING (Name)
ORDER BY sum_names.sum_qty DESC, names.Qty DESC
The sum_names table will contain:
Janet 23
Jones 15
James 9
You can specify more than one sorting condition:
SELECT * from names order by name, qty desc
Above query will sort by name and if names are equal then will sort by qty
If you want to select only higher qty for every user then use this query:
SELECT name, MAX(qty) FROM names GROUP BY name order by MAX(qty);

Limit the count using GROUP BY

I want to limit the count to 5 using COUNT(*) and group by but it returns all the rows.Consider I have a table names tbhits
tbhits
id | uname
------------------------
101 | john
101 | james
101 | henry
101 | paul
101 | jacob
101 | jaden
101 | steve
101 | lucas
102 | marie
SELECT id,COUNT(*) as 'hits' FROM tbhits GROUP BY id
returns
id | hits
--------------------
101 | 8
102 | 1
But I want the group by to limit maximum count to 5.
Say I have got 1000 rows I dont want to count them all, if rows are just greater than 5 then just display 5+
I tried using LIMIT 5 but it does not seem to work
SELECT id,COUNT(*) as 'hits' FROM tbhits GROUP BY id LIMIT 5 does not work.
I also used WHERE Clause
SELECT id,COUNT(*) as 'hits' FROM tbhits WHERE id = 101 GROUP BY id LIMIT 5
but it stil returns hits as 8.
id | hits
--------------------
101 | 8
Any help is greatly appreciated.
LIMIT is intended to limit the number of rows you'll get from your query. I suggest you use the COUNT function as follows :
SELECT id, CASE WHEN COUNT(*) < 6 then COUNT(*) ELSE '5+' END as 'hits'
FROM tbhits
GROUP BY id
More details about selecting the minimum of two numbers here, and here goes the sqlfiddle (consider providing it yourself next time).
Note that I went for 6 instead of '5+' on my first suggestion, because you should not, in my opinion, mix data types. But putting 6 is not a good solution either, because someone not aware of the trick will not notice it ('5+', at least, is explicit)
As far as performance is concerned, AFAIK you should not expect MySQL to do the optimization itself.
LIMIT on GROUP BY clause won't actually limit the counts, it will limit the rows being outputed.
Try using if statement to compare count result,
SELECT id,if(COUNT(*)>5,'5+',COUNT(*)) as 'hits'
FROM tbhits
GROUP BY id
O/p:
id | hits
--------------------
101 | 5+
102 | 1
Regarding performance issue, AFAIK GROUP BY will always lead to lead down performance and there is no direct way to limit counts in GROUP BY clause. You will have to go with either IF or CASE statement if you want solution from MySQL. Otherwise go with PHP itself.
Moreover you should have a look at GROUP BY optimization
As was already said LIMIT applies last in this case, thus after the grouping. What you want to do is modify the value that is selected once the grouping is done.
This will appropriately output "5+" if you have more than 5 records for your table.
SELECT id,
IF(COUNT(*)>5,"5+",COUNT(*)) AS 'count'
FROM Whatever GROUP BY id
See the SQL Fiddle here:
http://sqlfiddle.com/#!2/e381e/4
Try with this?
SELECT id,COUNT(*) as 'hits' FROM tbhits GROUP BY id
HAVING hits >= 5

Is it possible to perform calculations with mysql?

I have a DB table for photo ratings and want to retrieve the highest rated photos. I know I need to do this based on an average for the ratings sorted from highest to lowest. The DB table looks like this:
id rating rated_photo_id
-- ------ -------------
1 5 1
2 6 1
3 3 2
4 4 1
5 7 2
Is it efficient or even possible to perform this calculation in the SQL query? If not would it make sense to maintain a second table that stores the averages for each photo_id?
This is possible with almost all databases. Check out the aggregate functions of MySQL.
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
Specifically http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_avg for your question.
You DO NOT need a second table. The rating table has the information you need. Use MySQL aggregate functions with GROUP BY:
SELECT rated_photo_id, AVG(rating) AS AverageRating, COUNT(*) AS NumberOfRatings
FROM rating_table
GROUP BY rated_photo_id
ORDER BY AverageRating DESC
Output:
+----------------+---------------+-----------------+
| rated_photo_id | AverageRating | NumberOfRatings |
+----------------+---------------+-----------------+
| 1 | 5.0000 | 3 |
| 2 | 5.0000 | 2 |
+----------------+---------------+-----------------+
Yes, it's easy and efficient to calculate averages, assuming you've an index on the rated_photo_id column
select rated_photo_id, AVG(rating) as average_rating
from photos group by rated_photo_id order by average_rating desc
For a specific photo could specify an id:
select rated_photo_id, AVG(rating)
from photos where rated_photo_id = 2 group by rated_photo_id
Ideally your index would be (rated_photo_id, rating) to be covering for these queries--resulting in the fastest execution.
You should be able to just group by the photo id and get the average as the group is created.
SELECT rated_photo_id , AVG(rating) as rating
FROM photos
GROUP BY rated_photo_id
ORDER BY rating DESC