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

时间:2010-04-29 17:36:27

标签: mysql group-by grouping

在C#中就是这样:

table
   .GroupBy(row => row.SomeColumn)
   .Select(group => group
       .OrderBy(row => row.AnotherColumn)
       .First()
   )

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]

但它与MySQL不兼容。

15 个答案:

答案 0 :(得分:69)

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

SELECT somecolumn, anothercolumn 
  FROM sometable 
 WHERE id IN (
               SELECT min(id) 
                 FROM sometable 
                GROUP BY somecolumn
             );

答案 1 :(得分:19)

写作时

SELECT AnotherColumn
FROM Table
GROUP BY SomeColumn
;

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

这个“怪癖”与我想要的非常接近。所以我用它来得到我想要的结果:

SELECT * FROM 
(
 SELECT * FROM `table`
 ORDER BY AnotherColumn
) t1
GROUP BY SomeColumn
;

答案 2 :(得分:13)

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

select some_column, min(another_column)
  from i_have_a_table
 group by some_column

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

还要注意,通过这样做,你不能(轻松)获得其他值与生成的some_colum,another_column对相同的行!你需要lfagundes apprach和PK才能做到这一点!

答案 3 :(得分:6)

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

SELECT SomeColumn, MIN(AnotherColumn)
FROM YourTable
GROUP BY SomeColumn

一些希望有用的链接:

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

答案 4 :(得分:4)

来自MySQL 5.7 documentation

  

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

这意味着@Jader Dias的解决方案无处不在。

这是一个在tmp = conv(randn(1,len+nbmax),ckbeta); tmp = cumsum(tmp); CA = wkeep(tmp,len,'c'); for j=0:nblev-1 CD = 2^(j/2)*4^(-s)*2^(-j*s)*randn(1,len); len = 2*len-nbmax; CA = idwt(CA,CD,fs1,gs1,len); end fBm = wkeep(CA,L,'c'); fBm = fBm-fBm(1); 启用时可以使用的解决方案:

ONLY_FULL_GROUP_BY

答案 5 :(得分:2)

最佳性能且易于使用:

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

答案 6 :(得分:2)

我建议从MySql使用这种官方方式:

const parent = this.r2.createElement('div');  // container div to our stuff

this.jsonHTML.forEach((element) => {
  const attributes = Object.keys(element.attributes);
  const el = element.tagName && this.r2.createElement(element.tagName);
  const text = this.r2.createText(element.text);

  if (!el) {  // when there's no tag to create we just create text directly into the div.
    this.r2.appendChild(
      parent,
      text
    );
  } else { // otherwise we create it inside <a></a>
    this.r2.appendChild(
      el,
      text
    );

    this.r2.appendChild(
      parent,
      el
    );
  }
  
  if (attributes.length > 0) {
    attributes.forEach((name) => {
      if (el) {
        this.r2.setAttribute(el, name, element.attributes[name]); // just the value attribute for now

       if (name === 'value') { 
          this.r2.listen(el, 'click', () => {
            this.goto(element.attributes[name]); // event binding with property "value" as parameter to navigate to
          })
        }
      } else {
        throw new Error('no html tag specified as element...');
      }
    })
  }
})

this.r2.appendChild(this.container.nativeElement, parent); // div added to the DOM

通过这种方式,我们可以获得每篇文章的最高价格

答案 7 :(得分:1)

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

问题是在按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)

MySQL错误跟踪器中有FIRST()LAST() feature request,但它已在多年前关闭。

答案 8 :(得分:1)

另一种方法(不使用主键)将使用JSON函数:

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") )
  from sometable group by somecolumn

或低于5.7.22

select somecolumn, 
  json_unquote( 
    json_extract( 
      concat('["', group_concat(othercolumn separator '","') ,'"]') 
    ,"$[0]" ) 
  ) 
  from sometable group by somecolumn

排序(或过滤)可以在分组之前完成:

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") ) 
  from (select * from sometable order by othercolumn) as t group by somecolumn

...或分组后(当然):

select somecolumn, json_unquote( json_extract(json_arrayagg(othercolumn), "$[0]") ) as other 
  from sometable group by somecolumn order by other

诚然,它相当复杂,性能可能也不是很好(没有在大数据上进行测试,在我有限的数据集上也能很好地工作)。

答案 9 :(得分:1)

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;

在MySQL v8 +中,您可以使用窗口功能

答案 10 :(得分:0)

另一种方法

从在视图中工作的组中选择最大值

SELECT * FROM action a 
WHERE NOT EXISTS (
   SELECT 1 FROM action a2 
   WHERE a2.user_id = a.user_id 
   AND a2.action_date > a.action_date 
   AND a2.action_type = a.action_type
)
AND a.action_type = "CF"

答案 11 :(得分:0)

这个怎么样:

SELECT SUBSTRING_INDEX(
      MIN(CONCAT(OrderColumn, '|', IFNULL(TargetColumn, ''))
    ), '|', -1) as TargetColumn
FROM table
GROUP BY GroupColumn

答案 12 :(得分:0)

在Mysql中为每个组(按列排序)选择第一行。

我们有:

表格: mytable
我们按以下顺序排序的列: the_column_to_order_by
我们希望分组的列: the_group_by_column

这是我的解决方案。 内部查询为您提供一组唯一的行,这些行被选择为双键。 外部查询通过对两个键(使用AND)进行联接来联接同一表。

SELECT * FROM 
    ( 
        SELECT the_group_by_column, MAX(the_column_to_order_by) the_column_to_order_by 
        FROM mytable 
        GROUP BY the_group_by_column 
        ORDER BY MAX(the_column_to_order_by) DESC 
    ) as mytable1 
JOIN mytable mytable2 ON mytable2.the_group_by_column = 
mytablealiamytable2.the_group_by_column 
  AND mytable2.the_column_to_order_by = mytable1.the_column_to_order_by;

仅供参考:我根本没有考虑过效率,也不能以一种或另一种方式谈论。

答案 13 :(得分:0)

我最近发现了一个很酷的技巧来实现这一点。基本上只是从一个表中创建两个不同的子查询并将它们连接在一起。其中一个子查询根据分组进行聚合,另一个子查询仅获取每个分组项的第一个 DISTINCT 行。

当您将这些子查询连接在一起时,您将从每个组中获得第一个不同的项目,但也会为每个项目获得整个组中的聚合列。这与关闭 ONLY_FULL_GROUP_BY 的结果基本相同。

userId

答案 14 :(得分:-3)

为什么不使用MySQL LIMIT关键字?

SELECT [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]
LIMIT 1