Выберите лучшие N строк из M групп - PullRequest
3 голосов
/ 24 октября 2011

У меня есть эта таблица:

CREATE TABLE IF NOT EXISTS `catalog_sites` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `cat_id` int(10) unsigned NOT NULL,
  `date` datetime NOT NULL,
  `url` varchar(255) NOT NULL,
  `title` varchar(255) NOT NULL,
  `description` varchar(255) NOT NULL,
  `keywords` varchar(255) NOT NULL,
  `visited` int(10) unsigned NOT NULL,
  `shown` int(10) unsigned NOT NULL,
  `meta_try` int(1) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `url` (`url`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Я думаю, что моя проблема проста, но, кажется, не могу найти подходящее решение ..

Итак, это таблица с веб-сайтами, я хотел бы получить 6 сайтов в 6 различных категориях (cat_id, всего: 36 строк) с наивысшим рейтингом для каждой категории. Рейтинг рассчитывается как visited / shown.

Я должен получить 36 строк, содержащих 6 верхних категорий (их можно найти, отсортировав по AVG(visited / shown)), и 6 лучших сайтов в каждой из этих 6 категорий.

Если у вас есть идеи, как это может произойти по-другому, скажите, пожалуйста.

Ответы [ 2 ]

2 голосов
/ 24 октября 2011

Это должно дать вам то, что вы хотите, используя MySQL Variables, внутренний запрос будет предварительно вычислять рейтинг посещенных / показанных и используя порядок по условию, которое вы хотите ... По категории, самые высокие ранги ... и затем использование @vars будет держать @RankSeq последовательно 1-? по категориям. Из этого предварительного запроса (псевдоним PQ) запрос OUTER просто запрашивает предварительный запрос, где последовательность ранжирования URL-адреса <= 6 </p>

Чтобы гарантировать, что вы получаете только 6 верхних категорий, внутренний PreQuery также имеет предварительный запрос / лимит для «TopCategories» (псевдоним)

select
      PQ.URL,
      PQ.Cat_ID,
      PQ.Rank,
      PQ.URLRankSeq
   from 
      ( select 
              CS.cat_id,
              (CS.visited / CS.shown ) as Rank,
              CS.url,
              @RankSeq := if( @LastCat = CS.Cat_ID, @RankSeq +1, 1 ) URLRankSeq,
              @LastCat := CS.Cat_ID as ignoreIt
           from
              ( select cat_id, 
                       avg( visited / shown )
                   from catalog_sites
                   group by 1
                   order by 2 desc
                   limit 6 ) TopCategories

              JOIN catalog_sites CS
                 on TopCategories.Cat_ID = CS.Cat_ID,

              (select @RankSeq := 0, @LastCat = 0 ) SQLVars   
           order by 
              CS.cat_id,
              Rank ) PQ
    where
      PQ.URLRankSeq <= 6
1 голос
/ 24 октября 2011

Я попробовал твой пример, но он действительно не работает для меня, или я просто не знаю, как адаптировать его к моему случаю. Во всяком случае, я все еще новичок в том, что касается SQL, поэтому я не мог понять ваш запрос.

Однако мне удалось решить мою проблему. Это сложный и, возможно, худший из возможных подходов. Это тоже медленно, но я кеширую результаты, так что это не должно быть проблемой.

Вот мое решение:

SET @site_limit = 2;
SET @cat_limit = 6;

SET @row = 0;
SET @limiter = 0;
SET @last_cat = 0;

SELECT `cat_id`, `url`, `visited` / `shown` AS `rating`, @limiter := IF(@last_cat = `cat_id`, IF(@limiter >= @site_limit - 1, @limiter, @limiter + 1), 0) AS `limiter`, @last_cat := `cat_id` AS `last_cat`
FROM `catalog_sites`
WHERE `cat_id`
IN (
    SELECT `cat_id`
    FROM (
        SELECT `cat_id`, @row := @row + 1 AS `row`
        FROM (
            SELECT `cat_id`
            FROM `catalog_sites`
            GROUP BY `cat_id`
            ORDER BY AVG(`visited` / `shown`) DESC
        ) AS derived1
    ) AS derived2
    WHERE `row` <= @cat_limit
)
GROUP BY `cat_id`, `limiter`
ORDER BY `cat_id`, `rating` DESC
...