Reputation: 7775
I have a table like this: user_id hobbie1 hobbie2 hobbie3
1 ski soccer tv
1 skating tv sleep
1 tv ski sleep
1 tv read movies
(...)
And I want to get, for that user_id, the top 10 hobbies he has, ordered by occurences number. Yes, I know the database seems odd in terms of normalization, but this is a simplified example of the real thing :) The table has several rows with the same user_id and 3 columns with its hobbies.
In that example I should be able to get: hobbie count tv 4 sleep 2 ski 2 (...)
So, I'm thinking that I need to convert 1 row (1 ski soccer tv) to 3 rows (1 ski | 1 soccer | 1 tv) to then do my normal sql count, etc.
My solution is:
select hobbie, count(hobbie) from (
(select hobbie1 as hobbie from hobbies)
union all
(select hobbie2 as hobbie from hobbies)
union all
(select hobbie3 as hobbie from hobbies)
) AS b
group by b.hobbie order by 2 desc limit 10
But that doesn't seem to be optimized and uses UNION to solve the problem. Any better solution?
Upvotes: 1
Views: 194
Reputation: 21996
I like this problem, but If the goal is to do in a single query, I can't think of much better solution than you have.
To me, it begs for thinking out of the box:
Upvotes: 1
Reputation: 7961
There really isn't a good/nice way via one sql statement only.
Have a query that grabs all of the the hobbies and then loops over the result tabulating them. Something like (in php):
select hobbie1, hobbie2, hobbie3 from hobbies where user = ##
$tallies = array();
foreach ($result as $rs) // loops through the rows
for ($i = 1; $i <= 3; $i ++) // loops through the columns
if (array_key_exists($rs['hobbie'.$i], $tallies)
$tallies[$rs['hobbie'.$i]] += 1;
else
$tallies[$rs['hobbie'.$i]] = 1;
Then sort the array by values using asort()
asort($tallies);
They will now be smallest to largest so lets get the list of items and reverse it:
$tallies = array_reverse(array_keys($tallies));
You now have an array with the most popular hobby to the least. Hope that helps.
Upvotes: 1