Subselect 的 MySQL 排名 - Top N Results by Group

标签 mysql sql subquery

我需要获得每个国家/地区排名前 2 的名字的列表(来自客户和国家/地区表)。我搜索了很多,也找到了一些有效的答案,但无法得到正确的结果。

请在此处查看我的 SQL Fiddle:

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 session 变量”(基于 https://www.databasejournal.com/features/mysql/selecting-the-top-n-results-by-group-in-mysql.html)。

我的问题: country_rank 未正确填充,因此未给出正确的结果。我做错了什么?

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;

最佳答案

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;

关于Subselect 的 MySQL 排名 - Top N Results by Group,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53882309/

相关文章:

mysql - 通过连接删除

mysql - 在 SQL 查询中选择对编号

MySQL查询,(从子查询访问主查询)

MySQL: 无法创建表 (errno: 150)

mysql - 为什么我的表格需要这么长时间才能加载

php - MySQL PHP 从数据库表中获取倒数时间

sql - 用于将数据库更改从开发数据库同步到生产的工具?

mysql - 我如何将此查询转换为使用子查询的查询?

mysql - 为什么这个语句在作为子查询的一部分时有效?

mysql - MARIADB:索引不用于选择范围内的连接