开发者

Mysql query to count top ocurrences in 3 columns (considering it as 1 column only)

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?


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:

  • The unhelpful answer to "normalize" the data is, well, unhelpful. Obviously that's not an option.
  • How often do you need this data? How up to date does it need to be? Perhaps every night you could extract the needed data into a normalized table and run queries off of that? This is a common strategy for aggregate data that is just too hard to calculate.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜