Рейтинг MySQL подвыбора - лучшие N результатов по группам - PullRequest
0 голосов
/ 21 декабря 2018

Мне нужно получить список 2 лучших имен в каждой стране (из таблицы счетов и стран).Я много искал, а также нашел несколько правильных ответов, но не могу получить правильные результаты.

Пожалуйста, посмотрите мою скрипту SQL здесь:

http://sqlfiddle.com/#!9/cd1296/5

CREATE TABLE IF NOT EXISTS `country` (
  `id` int(6) unsigned NOT NULL,
  `iso` varchar(3) NOT NULL,
  `country_name` varchar(24) NOT NULL,
  PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;

INSERT INTO `country` (`id`, `iso`,`country_name`) VALUES
  ('1', 'DEU','Germany'),
  ('2', 'USA','United States'),
  ('3', 'CAN','Canada'),
  ('4', 'JPN','Japan');

CREATE TABLE IF NOT EXISTS `accounts` (
  id int(6) unsigned NOT NULL,
  name varchar(50) NOT NULL,
  iso3 varchar(3) NOT NULL,
  PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

INSERT INTO `accounts` (`id`,`name`, `iso3`) VALUES
  ('1', 'Hans', 'DEU'),
  ('2', 'Willi', 'DEU'),
  ('3', 'Peter', 'DEU'),
  ('4', 'Susanne', 'DEU'),
  ('5', 'John', 'USA'),
  ('6', 'Jane', 'USA'),
  ('7', 'Peter', 'USA'),
  ('8', 'Paul', 'USA'),
  ('9', 'Mary', 'USA'),
  ('10', 'Gerard', 'CAN'),
  ('11', 'Mirelle', 'CAN'),
  ('12', 'Hiko', 'JPN'),
  ('13', 'Miko', 'JPN'),
  ('14', 'Susanne', 'DEU'),
  ('15', 'Peter', 'DEU'),
  ('16', 'John', 'USA'),
  ('17', 'Paul', 'USA'),
  ('18', 'Susanne', 'DEU'),
  ('19', 'Bob', 'DEU'),
  ('20', 'John', 'USA'),
  ('21', 'Paul', 'USA'),
  ('33', 'Gerard', 'CAN'),
  ('22', 'Maribelle', 'CAN'),  
  ('23', 'Gerd', 'CAN'),
  ('24', 'Mira', 'CAN'),
  ('25', 'Huko', 'JPN'),
  ('26', 'Hako', 'JPN'),
  ('27', 'Hiko', 'JPN'),
('28', 'Jon', 'USA'),
('29', 'Jim', 'USA'),
('30', 'John', 'USA'),
('31', 'JJ', 'USA'),
('32', 'Bob', 'USA'),
('34', 'Bob', 'USA'),
('35', 'Miko', 'JPN'),
('36', 'Miko', 'JPN');

Использование этого оператора приводит список в правильном порядке, но не останавливается после второго результата:

SELECT country_name, iso, name, COUNT(name) AS name_count
 FROM accounts
 JOIN country ON country.iso = accounts.iso3
 GROUP BY country.iso,  name
 ORDER BY country.iso ASC, name_count DESC;

Как и предлагалось в других вопросах / ответах, решение могло бы использовать «переменные сеанса MySQL» (на основе https://www.databasejournal.com/features/mysql/selecting-the-top-n-results-by-group-in-mysql.html).

Моя проблема: Country_rank заполнен неправильнои так не дает результатов corrent. Что я делаю не так?

SET @current_country = ""; 
SET @country_rank = 0; 

 SELECT country_name, name, name_count, rank
 FROM
 (
    SELECT country_name, iso, name, COUNT(name) AS name_count,
    @country_rank := IF( @current_country = iso, 
                         @country_rank + 1, 
                         1 
                       ) AS rank, 
    @current_country := iso 
    FROM accounts
    JOIN country ON country.iso = accounts.iso3
    GROUP BY country.iso,  name
    ORDER BY country.iso ASC, name_count DESC
) AS ranked
WHERE rank<=2;

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

MySQL не гарантирует порядок вычисления выражений в SELECT.Следовательно, опасно определять переменную в одном выражении, а затем использовать ее в другом.То есть назначение переменных и их использование должны быть в одном выражении.

Проблемы могут быть прерывистыми, поэтому код может выглядеть так, как будто он работает в одном контексте, но не работает в другом.Поэтому я бы рекомендовал написать это как:

SELECT country_name, name, name_count, rank
FROM (SELECT country_name, iso, name, name_count,
             (@rn := IF(@c = iso, @rn + 1,
                        IF(@c := iso, 1, 1)
                       )
             ) as rank
      FROM (SELECT c.country_name, c.iso, a.name, COUNT(*) AS name_count
            FROM accounts a JOIN
                 country c
                 ON c.iso = a.iso3
            GROUP BY country.iso,  name
            ORDER BY c.country_name, c.iso ASC, name_count DESC
           ) c CROSS JOIN
           (SELECT @c := '', @rn := 0) params
      ) c
WHERE rank <= 2;
0 голосов
/ 21 декабря 2018

Вы должны выполнить группировку в подзапросе, чтобы ранжирование проводилось по сгруппированным результатам.

SELECT country_name, name, name_count, rank
FROM (
    SELECT country_name, iso, name, name_count,
        @country_rank := IF( @current_country = iso, 
                             @country_rank + 1, 
                             1 
                           ) AS rank, 
        @current_country := iso 
    FROM (
        SELECT country_name, iso, name, COUNT(name) AS name_count
        FROM accounts
        JOIN country ON country.iso = accounts.iso3
        GROUP BY country.iso,  name
        ORDER BY country.iso ASC, name_count DESC
    ) AS ordered
) AS ranked
CROSS JOIN (SELECT @country_rank = 0, @current_country = '') AS vars
WHERE rank<=2;

Fiddle

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...