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

Posted

技术标签:

【中文标题】MySQL:选择 N 行,但在一列中只有唯一值【英文标题】:MySQL: Select N rows, but with only unique values in one column 【发布时间】:2010-09-16 11:36:28 【问题描述】:

给定这个数据集:

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 行。我有一个关于“City, Birthyear”的索引,还有一个关于“Birthyear”的索引。

注意:这与...有关。

Selecting unique rows in a set of two possibilities SQL Query to get latest price

【问题讨论】:

注意:这不是我要解决的实际问题,而是一个例子。我需要两个不同工作的解决方案:a)在每个房间中找到价格最高的物品 - 如果有几个价格相同:获取最新的。 b) 从队列中获取 10 个作业(按优先级排序),但每个客户只能获取一个。 【参考方案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,但我现在把它留在那里,以防万一。以后可能会删除它。

【讨论】:

【参考方案2】:

不漂亮,但也应该与具有相同出生日期的多人一起使用:

测试数据:

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

【讨论】:

这个也有效。但是表格中有很多行,它变得非常slooooow :) @BlaM,为什么这么慢?解释?【参考方案3】:

可能不是最优雅的解决方案,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  |
+-----------------+-------------+------+----+

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

【讨论】:

这适用于给出的示例数据,但“在现实生活中”可能有两个人的出生年份相同。此查询将返回所有这些。 :( 好点,按城市分组应该排序,现在更新答案。如果您在同一个城市有 2 个人出生年份相同,您只会看到其中一个 - 可能是第一个输入的,这取决于 mysql 在您的桌子上使用的默认排序。 其实你不会。您将收到一条错误消息,因为您不能在“未分组”列上没有任何聚合函数的情况下使用 GROUP BY。 嗯。在 mysql 5.0.51 上尝试过,效果很好。您可以在外部选择中添加一个 count(*) 以避免此错误,这也会显示您是否在同一出生年份的城市中 > 1 人。用这个额外的位更新答案 呃...它确实有效。哇。我以为 GROUP BY 总是需要聚合函数,但似乎我错了……【参考方案4】:

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

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

【讨论】:

这与我所做的最接近,即使我会尝试在某个地方引入 ID,因为在同一个城市可能有两个年龄相同的人。 顺便说一句:在这种情况下,选择两个人中的哪一个并不重要。它应该只是“一个”。【参考方案5】:

类似的东西?

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)

【讨论】:

这是 T-SQL,我知道。应该很容易适应 MySQL 或任何其他方言。 MySQL 不支持“IN”-子查询中的 TOP/LIMIT

以上是关于MySQL:选择 N 行,但在一列中只有唯一值的主要内容,如果未能解决你的问题,请参考以下文章

选择小于另一列中特定值的唯一值

MYSQL 在一列上选择两个值

从表中选择行,其中具有相同 id 的另一个表中的行在另一列中具有特定值

Excel - 合并具有共同值的行并将差异连接在一列中

如何在一列中获得不同的多列行

SQL选择一列中具有重复值的所有行