如何为MySQL中的每个组选择第一行?

Jad*_*ias 59 mysql grouping group-by

在C#中它会是这样的:

table
   .GroupBy(row => row.SomeColumn)
   .Select(group => group
       .OrderBy(row => row.AnotherColumn)
       .First()
   )
Run Code Online (Sandbox Code Playgroud)

Linq-To-Sql将其转换为以下T-SQL代码:

SELECT [t3].[AnotherColumn], [t3].[SomeColumn]
FROM (
    SELECT [t0].[SomeColumn]
    FROM [Table] AS [t0]
    GROUP BY [t0].[SomeColumn]
    ) AS [t1]
OUTER APPLY (
    SELECT TOP (1) [t2].[AnotherColumn], [t2].[SomeColumn]
    FROM [Table] AS [t2]
    WHERE (([t1].[SomeColumn] IS NULL) AND ([t2].[SomeColumn] IS NULL))
      OR (([t1].[SomeColumn] IS NOT NULL) AND ([t2].[SomeColumn] IS NOT NULL)
        AND ([t1].[SomeColumn] = [t2].[SomeColumn]))
    ORDER BY [t2].[AnotherColumn]
    ) AS [t3]
ORDER BY [t3].[AnotherColumn]
Run Code Online (Sandbox Code Playgroud)

但它与MySQL不兼容.

lfa*_*des 73

我的答案只基于你帖子的标题,因为我不知道C#并且不理解给定的查询.但是在MySQL中,我建议你尝试子选择.首先获取一组有趣列的主键,然后从这些行中选择数据:

SELECT somecolumn, anothercolumn 
  FROM sometable 
 WHERE id IN (
               SELECT min(id) 
                 FROM sometable 
                GROUP BY somecolumn
             );
Run Code Online (Sandbox Code Playgroud)

  • 好吧,总是有一个主键是一个好习惯,理论上如果你没有主键,整行的集合应该是你的主键(尽管MySQL会接受一个没有重复行的主键的表) . (6认同)
  • 如果您正在评估大型记录集,IN往往会非常慢.如果可以使用它,您通常可以从EXISTS获得更好的性能.在许多情况下(例如这个)你可以使用更快的INNER JOIN.SELECT c1,c2 FROM t1 INNER JOIN(SELECT min(c2)c2 FROM t1)a1 ON t1.c2 = a1.c2 (2认同)

Jad*_*ias 20

当我写作

SELECT AnotherColumn
FROM Table
GROUP BY SomeColumn
;
Run Code Online (Sandbox Code Playgroud)

有用.IIRC在其他RDBMS中这样的语句是不可能的,因为不属于分组键的列被引用而没有任何类型的聚合.

这种"怪癖"与我想要的非常接近.所以我用它来得到我想要的结果:

SELECT * FROM 
(
 SELECT * FROM `table`
 ORDER BY AnotherColumn
) t1
GROUP BY SomeColumn
;
Run Code Online (Sandbox Code Playgroud)

  • 讨论为什么第一个语句有效:http://stackoverflow.com/questions/1225144/why-does-mysql-allow-group-by-queries-without-aggregate-functions.显然启动MySQL 5.7.5这将被禁用默认情况下,http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_only_full_group_by (4认同)

lex*_*exu 17

这是您可以尝试的另一种方式,不需要该ID字段.

select some_column, min(another_column)
  from i_have_a_table
 group by some_column
Run Code Online (Sandbox Code Playgroud)

我仍然同意lfagundes你应该添加一些主键..

还要注意,通过执行此操作,您不能(轻松)获取其他值与生成的some_colum,another_column对相同的行!你需要lfagundes apprach和PK来做到这一点!


Dav*_*d M 6

您应该使用一些聚合函数来获取所需的AnotherColumn的值.也就是说,如果您想为SomeColumn的每个值(数字或词典)获得AnotherColumn的最低值,您可以使用:

SELECT SomeColumn, MIN(AnotherColumn)
FROM YourTable
GROUP BY SomeColumn
Run Code Online (Sandbox Code Playgroud)

一些希望有用的链接:

http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html

http://www.oreillynet.com/databases/blog/2007/05/debunking_group_by_myths.html

  • 要使用的聚合函数不是`MIN`而是`FIRST`,这是MySQL所缺乏的。 (3认同)

Ric*_*dos 6

SELECT
    t1.*

FROM
    table_name AS t1

    LEFT JOIN table_name AS t2 ON (
        t2.group_by_column = t1.group_by_column
        -- group_by_column is the column you would use in the GROUP BY statement
        AND
        t2.order_by_column < t1.order_by_column
        -- order_by_column is column you would use in the ORDER BY statement
        -- usually is the autoincremented key column
    )

WHERE
    t2.group_by_column IS NULL;
Run Code Online (Sandbox Code Playgroud)

使用 MySQL v8+,您可以使用窗口函数

  • 这是我在 5.7+ 启用“ONLY_FULL_GROUP_BY”的情况下可以为我的用例工作的唯一答案。我们有一个 PK,无论出于什么原因,MySQL 5.7 一直认为它在功能上不依赖于我们需要“GROUP BY”的列。其他答案似乎非常具体地针对他们的特定问题或需要 SQL 变量...这是一个直接查询,并且对于许多用途来说足够通用。我唯一需要改变的是“ORDER BY”列的不等式,但这取决于需要。 (2认同)

unc*_*bob 6

我建议使用 MySql 的官方方式:

SELECT article, dealer, price
FROM   shop s1
WHERE  price=(SELECT MAX(s2.price)
              FROM shop s2
              WHERE s1.article = s2.article
              GROUP BY s2.article)
ORDER BY article;
Run Code Online (Sandbox Code Playgroud)

这样我们就可以得到每件物品的最高价格


Nic*_*lai 5

来自MySQL 5.7 文档

MySQL 5.7.5 及更高版本实现了功能依赖检测。如果启用了 ONLY_FULL_GROUP_BY SQL 模式(默认情况下是这样),MySQL 将拒绝选择列表、HAVING 条件或 ORDER BY 列表引用非聚合列的查询,这些列既不在 GROUP BY 子句中命名,也不在功能上依赖它们.

这意味着@Jader Dias 的解决方案不会在任何地方都有效。

这是一个在ONLY_FULL_GROUP_BY启用时有效的解决方案:

SET @row := NULL;
SELECT
    SomeColumn,
    AnotherColumn
FROM (
    SELECT
        CASE @id <=> SomeColumn AND @row IS NOT NULL 
            WHEN TRUE THEN @row := @row+1 
            ELSE @row := 0 
        END AS rownum,
        @id := SomeColumn AS SomeColumn,
        AnotherColumn
    FROM
        SomeTable
    ORDER BY
        SomeColumn, -AnotherColumn DESC
) _values
WHERE rownum = 0
ORDER BY SomeColumn;
Run Code Online (Sandbox Code Playgroud)


Lar*_*röm 5

我在答案中没有看到以下解决方案,所以我想我会把它放在那里。

问题是AnotherColumn在按 分组的所有组中按顺序选择第一行SomeColumn

以下解决方案将在 MySQL 中执行此操作。id必须是一个唯一的列,它不能包含包含-(我用作分隔符)的值。

select t1.*
from mytable t1
inner join (
  select SUBSTRING_INDEX(
    GROUP_CONCAT(t3.id ORDER BY t3.AnotherColumn DESC SEPARATOR '-'),
    '-', 
    1
  ) as id
  from mytable t3
  group by t3.SomeColumn
) t2 on t2.id = t1.id


-- Where 
SUBSTRING_INDEX(GROUP_CONCAT(id order by AnotherColumn desc separator '-'), '-', 1)
-- can be seen as:
FIRST(id order by AnotherColumn desc)

-- For completeness sake:
SUBSTRING_INDEX(GROUP_CONCAT(id order by AnotherColumn desc separator '-'), '-', -1)
-- would then be seen as:
LAST(id order by AnotherColumn desc)
Run Code Online (Sandbox Code Playgroud)

有一个功能要求FIRST(),并LAST()在MySQL的bug跟踪系统,但它被关闭多年了。


Mar*_*rík 5

最佳性能且易于使用:

SELECT id, code,
SUBSTRING_INDEX( GROUP_CONCAT(price ORDER BY id DESC), ',', 1) first_found_price
FROM stocks
GROUP BY code
ORDER BY id DESC
Run Code Online (Sandbox Code Playgroud)

  • 这是一个有趣的解决方案。谢谢。 (2认同)

Ger*_*ade 5

rtribaldos 提到,在较新的数据库版本中,可以使用窗口函数。\n这是一个对我有用的代码,并且与 Martin Zwar\xc3\xadk 的substring_index -解决方案(在 Mariadb 10.5.16 中)
一样快:

\n
SELECT group_col, order_col FROM (\n  SELECT group_col, order_col\n  , ROW_NUMBER() OVER(PARTITION BY group_col ORDER BY order_col) rnr \n  FROM some_table\n  WHERE <some_condition>\n) i\nWHERE rnr=1;\n
Run Code Online (Sandbox Code Playgroud)\n