MySQL:选择 N 行,但一列中只有唯一值

问题描述 投票:0回答:5

给定这个数据集:

ID  Name            City            Birthyear
1   Egon Spengler   New York        1957
2   Mac Taylor      New York        1955
3   Sarah Connor    Los Angeles     1959
4   Jean-Luc Picard La Barre        2305
5   Ellen Ripley    Nostromo        2092
6   James T. Kirk   Riverside       2233
7   Henry Jones     Chicago         1899

我需要找到 3 个最年长的人,但每个城市只能找到一个。

如果只是三个最年长的话,那就是……

  • 亨利琼斯/芝加哥
  • 麦克泰勒/纽约
  • 埃贡·斯宾格勒/纽约

然而,由于 Egon Spengler 和 Mac Taylor 都位于纽约,Egon Spengler 会退出,而下一位(Sarah Connor / 洛杉矶)会加入。

有什么优雅的解决方案吗?

更新:

目前 PConroy 的变体是最好/最快的解决方案:

SELECT P.*, COUNT(*) AS ct
   FROM people P
   JOIN (SELECT MIN(Birthyear) AS Birthyear
              FROM people 
              GROUP by City) P2 ON P2.Birthyear = P.Birthyear
   GROUP BY P.City
   ORDER BY P.Birthyear ASC 
   LIMIT 10;

他最初使用“IN”的查询对于大数据集来说非常慢(5 分钟后中止),但是将子查询移动到 JOIN 会大大加快速度。大约需要 0.15 秒。我的测试环境中有 1 mio 行。我有一个关于“城市,出生年份”的索引,还有一个关于“出生年份”的索引。

注意:这与...相关

sql mysql unique aggregate-functions min
5个回答
19
投票

可能不是最优雅的解决方案,并且

IN
的性能可能会在较大的桌子上受到影响。

嵌套查询获取每个城市的最小值

Birthyear
。只有具有此
Birthyear
的记录才会在外部查询中匹配。按年龄排序,然后限制为 3 个结果,您将得到 3 位最年长的人,他们也是所在城市最年长的人(Egon Spengler 退出..)

SELECT Name, City, Birthyear, COUNT(*) AS ct
FROM table
WHERE Birthyear IN (SELECT MIN(Birthyear)
               FROM table
               GROUP by City)
GROUP BY City
ORDER BY Birthyear DESC LIMIT 3;

+-----------------+-------------+------+----+
| name            | city        | year | ct |
+-----------------+-------------+------+----+
| Henry Jones     | Chicago     | 1899 | 1  |
| Mac Taylor      | New York    | 1955 | 1  |
| Sarah Connor    | Los Angeles | 1959 | 1  |
+-----------------+-------------+------+----+

Edit - 在外部查询中添加了

GROUP BY City
,因为具有相同出生年份的人会返回多个值。如果超过一个人具有该最小值
Birthyear
,则对外部查询进行分组可确保每个城市仅返回一个结果。
ct
列将显示城市中是否存在多个具有该
Birthyear

的人

3
投票

这可能不是最优雅和最快的解决方案,但它应该可行。我期待看到真正的数据库大师的解决方案。

select p.* from people p,
(select city, max(age) as mage from people group by city) t
where p.city = t.city and p.age = t.mage
order by p.age desc

2
投票

有这样的事吗?

SELECT
  Id, Name, City, Birthyear
FROM
  TheTable
WHERE
  Id IN (SELECT TOP 1 Id FROM TheTable i WHERE i.City = TheTable.City ORDER BY Birthyear)

1
投票

不太漂亮,但应该也可以与具有相同 dob 的多个人一起使用:

测试数据:

select id, name, city, dob 
into people
from
(select 1 id,'Egon Spengler' name, 'New York' city , 1957 dob
union all select 2, 'Mac Taylor','New York', 1955
union all select 3, 'Sarah Connor','Los Angeles', 1959
union all select 4, 'Jean-Luc Picard','La Barre', 2305
union all select 5, 'Ellen Ripley','Nostromo', 2092
union all select 6, 'James T. Kirk','Riverside', 2233
union all select 7, 'Henry Jones','Chicago', 1899
union all select 8, 'Blah','New York', 1955) a

查询:

select 
    * 
from 
    people p
    left join people p1
    ON 
        p.city = p1.city
        and (p.dob > p1.dob and p.id <> p1.id)
        or (p.dob = p1.dob and p.id > p1.id)
where
    p1.id is null
order by 
    p.dob

1
投票

@BlaM

已更新 刚刚发现用USING代替ON很好。它将删除结果中的重复列。

SELECT P.*, COUNT(*) AS ct
   FROM people P
   JOIN (SELECT City, MIN(Birthyear) AS Birthyear
              FROM people 
              GROUP by City) P2 USING(Birthyear, City)
   GROUP BY P.City
   ORDER BY P.Birthyear ASC 
   LIMIT 10;

原帖

嗨,我尝试使用您更新的查询,但我得到了错误的结果,直到我添加了额外的条件来加入(也在加入选择中添加了额外的列)。转移到您的查询,我正在使用这个:

SELECT P.*, COUNT(*) AS ct
   FROM people P
   JOIN (SELECT City, MIN(Birthyear) AS Birthyear
              FROM people 
              GROUP by City) P2 ON P2.Birthyear = P.Birthyear AND P2.City = P.City
   GROUP BY P.City
   ORDER BY P.Birthyear ASC 
   LIMIT 10;

理论上你不需要最后一个 GROUP BY P.City,但我暂时把它留在那里,以防万一。稍后可能会删除它。

© www.soinside.com 2019 - 2024. All rights reserved.