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
Related
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
I'm having trouble using rollup in mysql. I want a table that looks something like this:
Person | Count
John | 3
Sam | 2
Total | 5
The code I wrote:
SELECT person, count(*), IFNULL(count(*),"Total") AS Count FROM ProductsSold GROUP BY
person WITH ROLLUP;
But Instead total is staying null and all my counts say BLOB in workbench.
My guess is you want to rename the person NULL which holds the ROLLUP result to Total. That means you have do your IFNULL construct on the person column:
SELECT IFNULL(person,"Total") as person, count(*) AS Count
FROM ProductsSold
GROUP BY person WITH ROLLUP
makes:
John 3
Sam 2
Total 5
I am trying to get the maximum value out of a aggregate function, and then also get the min value out of a Price column which comes back in results.
id | discount | price
1 | 60 | 656
2 | 60 | 454
3 | 60 | 222
4 | 30 | 335
5 | 30 | 333
6 | 10 | 232
So in above table, I would like to separate Minimum Price vs Highest Discount.
This is the result I should be seeing:
id | discount | price
3 | 60 | 222
5 | 30 | 333
6 | 10 | 232
As you can see, its taken discount=60 group and separated the lowest price - 222, and the same for all other discount groups.
Could someone give me the SQL for this please, something like this -
SELECT MAX(discount) AS Maxdisc
, MIN(price) as MinPrice
,
FROM mytable
GROUP
BY discount
However, this doesnt separate the minimum price for each group. I think i need to join this table to itself to achieve that. Also, the table contains milions of rows, so the sql needs to be fast. One flat table.
This question is asked and answered with tedious regularity in SO. If only the algorithm was better at spotting duplicates. Anyway...
SELECT x.*
FROM my_table x
JOIN
( SELECT discount,MIN(price) min_price FROM my_table GROUP BY discount) y
ON y.discount = x.discount
AND y.min_price = x.price;
In your query, you cannot group by discount and then maximize the discount value.
This should get you the result you are looking for..
SELECT Max(ID) AS ID, discount, MIN(price) as MinPrice, FROM mytable GROUP BY discount
If you do not need the id, yo would do:
select discount, min(price) as minprice
from table t
group by discount;
If you want other columns in the row, you can either join back to the original table or use the substring_index()/group_concat() trick:
select substring_index(group_concat(id order by price), ',', 1) as id,
discount, min(price)
from table t
group by discount;
This will not always work because the intermediate result for group_concat() can overflow if there are too many matches within a column. This is controlled by a system parameter, which could be made bigger if necessary.
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
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).