dze*_*kob 2 mysql group-by greatest-n-per-group mysql-8.0
我是一个 SQL 初学者,遇到了以下问题。考虑一个包含国家、该国家内的城市及其人口数据的表格:
CREATE TABLE cities (
country VARCHAR(20),
city VARCHAR(20),
population INT
);
INSERT INTO cities VALUES
('Italy', 'Milano', 1000000),
('Italy', 'Rome', 2000000),
('Italy', 'Bologna', 800000),
('Poland', 'Warszawa', 1000000),
('Poland', 'Wroclaw', 700000);
Run Code Online (Sandbox Code Playgroud)
我想编写一个查询,返回国家/地区名称及其最大城市的人口和城市名称本身。前两个字段使用 很简单GROUP BY。但是,我不知道如何包含最大城市的名称。我试过:
SELECT
country, MAX(population), city
FROM
cities
WHERE
population = (SELECT
MAX(population)
FROM
cities
)
GROUP BY country, city;
Run Code Online (Sandbox Code Playgroud)
但这只选择了一项记录(有关罗马的记录)。我希望查询为每个国家/地区组返回一条记录,即本例中为罗马和华沙。条件WHERE不能是:
population = (SELECT
MAX(population)
FROM
cities
GROUP BY country)
Run Code Online (Sandbox Code Playgroud)
因为这样子查询返回两行,导致主查询中出现错误1242。
执行此操作的一个简单方法是使用ROW_NUMBER()(手动索引和函数描述)窗口函数 - 无需使用JOIN. 下面的所有代码都可以在此处的小提琴上找到:
我设置了表格(见下文 - 添加了两个索引),然后用您的数据填充它(见小提琴)。
\nCREATE TABLE cities \n(\n country VARCHAR(20),\n city VARCHAR(20),\n population INT,\n\n CONSTRAINT cities_pk PRIMARY KEY (country, city),\n KEY cities_city_ix (city, population)\n \n);\nRun Code Online (Sandbox Code Playgroud)\n然后,第一件事是运行(这是为了解释):
\nSELECT\n country, city, population, \n ROW_NUMBER() OVER (PARTITION BY country ORDER BY country, population DESC) AS rn\nFROM \n cities;\nRun Code Online (Sandbox Code Playgroud)\n结果:
\ncountry city population rn\n Italy Rome 2000000 1 <--- Italy\'s biggest city\n Italy Milano 1000000 2\n Italy Bologna 800000 3\n Poland Warszawa 1000000 1 <--- Poland\'s " "\n Poland Wroclaw 700000 2\nRun Code Online (Sandbox Code Playgroud)\n因此,现在运行此 SQL,从上面的查询中挑选出最热门的城市作为“pops”子查询(或派生表):
\nSELECT\n country, city, population\nFROM\n(\n SELECT\n country, city, population, \n ROW_NUMBER() OVER (PARTITION BY country ORDER BY country, population DESC) AS rn\n FROM \n cities\n) AS pops\nWHERE rn = 1\nORDER BY country;\nRun Code Online (Sandbox Code Playgroud)\n结果:
\ncountry city population\nItaly Rome 2000000\nPoland Warszawa 1000000\nRun Code Online (Sandbox Code Playgroud)\n窗口函数非常强大,您多次学习它们所付出的努力将会得到回报。比orROW_NUMBER()更强大,因为它不仅允许您选择前 1 个,还允许选择前 2、3...&c。再次,阅读并练习一下,直到您掌握它们!FIRST_VALUE()LAST_VALUE()
顺便说一句,我会将这些行附加到表定义的底部 - PK 和INDEX(或KEY- 少输入 :-) )。
CONSTRAINT cities_pk PRIMARY KEY (country, city),\nKEY cities_city_ix (city, population)\nRun Code Online (Sandbox Code Playgroud)\n这些似乎产生了一个很好的计划 - 所有索引,没有表扫描!参见小提琴。
\nEXPLAIN\n-> Sort: pops.country (cost=0.00..0.00 rows=0)\n\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87-> Index lookup on pops using <auto_key0> (rn=1)\n\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87-> Materialize (cost=0.00..0.00 rows=0)\n\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87-> Window aggregate: row_number() OVER (PARTITION BY cities.country ORDER BY cities.country,cities.population desc ) \n\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87-> Sort: cities.country, cities.country, cities.population DESC (cost=0.75 rows=5)\n\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87\xe2\x80\x87-> Index scan on cities using cities_city_ix\nRun Code Online (Sandbox Code Playgroud)\n警告:这EXPLAIN是在最小的数据集上完成的 - 我建议您在您的硬件和数据上测试此处的所有答案,以便您利用最佳解决方案。