Mysql 在SQL中,如何为每个组选择前两行

Mysql 在SQL中,如何为每个组选择前两行,mysql,sql,group-by,greatest-n-per-group,Mysql,Sql,Group By,Greatest N Per Group,我有如下表格: NAME SCORE ----------------- willy 1 willy 2 willy 3 zoe 4 zoe 5 zoe 6 这是我的建议 group by的聚合函数只允许我获得每个名称的最高分数。 我想查询每个名字的最高2分,我该怎么做 我的预期产出是 NAME SCORE ----------------- willy 2 willy 3

我有如下表格:

NAME    SCORE
-----------------
willy       1
willy       2
willy       3
zoe         4
zoe         5
zoe         6
这是我的建议

group by的聚合函数只允许我获得每个名称的最高分数。 我想查询每个名字的最高2分,我该怎么做

我的预期产出是

NAME    SCORE
-----------------
willy       2
willy       3
zoe         5
zoe         6

为此,你可以这样做-


但为了获得前2个查询,您应该使用一个ID,然后对ID运行limit,如0,2。

在MySQL中,您可以使用用户定义的变量来获取每个组中的行号:

select name, score
from
(
  SELECT name,
    score,
    (@row:=if(@prev=name, @row +1, if(@prev:= name, 1, 1))) rn
  FROM test123 t
  CROSS JOIN (select @row:=0, @prev:=null) c
  order by name, score desc 
) src
where rn <= 2
order by name, score;

请参见

您可以这样做:

SET @num :=0, @name :='';   
SELECT name, score,
    @num := IF( @name= name, @num +1, 1 ) AS row_number,
    @name := name AS dummy
FROM test
GROUP BY name, score
HAVING row_number <=2

如果您不介意增加一列,则可以使用以下代码:

SELECT Name, Score, rank() over(partition by Name order by Score DESC) as rank
From Table
Having rank < 3;
Rank函数为每个分区提供秩,在您的情况下是name

使用此查询

select * from fruits 
where type = 'orange'  
order by price limit 2
解决方案如下:

恐怕这不是我所期望的,我只是给出了一种方法,您可以简单地做到这一点,如果您为每一行主键维护ID,它将工作得更多,您将拥有更多的功能。由于您需要它的长代码,而且将来在它上面使用其他任何东西都会比较困难。感谢您提供此解决方案,我对SQL还是新手。希望我以后能理解这一点:@waitingkuo不幸的是MySQL没有窗口功能,可以让您轻松地为组中的每一行分配一个行号。@BlueFoots感谢这是一个非常好的解决方案,即使在30k左右的行上,它也工作得非常快,我以前使用联接的解决方案非常慢。这样安全吗?MySQL声明涉及用户变量的表达式的求值顺序未定义。这是否意味着@prev:=name可以在case语句之前进行求值,因此case语句将人为地为true?还是我遗漏了什么?请参阅,但这可能会导致性能问题。有没有其他更快的方法来实现此查询?这确实会导致相当严重的性能问题子选择是二次的。这可以线性完成,请参阅MySQL查询以获得前2名。我得到错误:Msg 156,级别15,状态1,第5行关键字“order”附近的语法不正确。NameGood第一个答案后不应该有逗号!请先测试代码,然后在插入后发布缺少的分号。使用注释和使用过的概念(例如,使用和)进行解释。如果使用Oracle SQL,请参阅
SELECT * FROM (   
    SELECT  VD.`cat_id` ,  
       @cat_count := IF( (@cat_id = VD.`cat_id`), @cat_count + 1, 1 ) AS 'DUMMY1', 
       @cat_id := VD.`cat_id` AS 'DUMMY2',
       @cat_count AS 'CAT_COUNT'   
     FROM videos VD   
     INNER JOIN categories CT ON CT.`cat_id` = VD.`cat_id`  
       ,(SELECT @cat_count :=1, @cat_id :=-1) AS CID  
     ORDER BY VD.`cat_id` ASC ) AS `CAT_DETAILS`
     WHERE `CAT_COUNT` < 4

------- STEP FOLLOW ----------  
1 . select * from ( 'FILTER_DATA_HERE' ) WHERE 'COLUMN_COUNT_CONDITION_HERE' 
2.  'FILTER_DATA_HERE'   
    1. pass 2 variable @cat_count=1 and  @cat_id = -1  
    2.  If (@cat_id "match" column_cat_id value)  
        Then  @cat_count = @cat_count + 1    
        ELSE @cat_count = 1      
    3. SET @cat_id = column_cat_id    

 3. 'COLUMN_COUNT_CONDITION_HERE'   
    1. count_column < count_number    

4. ' EXTRA THING '
   1. If you want to execute more than one statement inside " if stmt "
   2. IF(condition, stmt1 , stmt2 )
      1. stmt1 :- CONCAT(exp1, exp2, exp3) 
      2. stmt2 :- CONCAT(exp1, exp2, exp3) 
   3. Final "If" Stmt LIKE 
      1. IF ( condition , CONCAT(exp1, exp2, exp3) , CONCAT(exp1, exp2, exp3) )    
select * from fruits 
where type = 'orange'  
order by price limit 2
insert into test values('willy',1)
insert into test values('willy',2)
insert into test values('willy',3)
insert into test values('zoe',4)
insert into test values('zoe',5)
insert into test values('zoe',6)


;with temp_cte
as (
    select Name, Score,
       ROW_NUMBER() OVER (
          PARTITION BY Name
          ORDER BY Score desc
       ) row_num
    from test
)
select * from temp_cte
where row_num < 3