简体   繁体   English

mySQL - 选择前n行,按2列分组

[英]mySQL - select the top n rows, grouped by 2 columns

I've found existing questions with the latest/lowest single row over several fields, or latest n rows over a single field, but not the two together 我已经找到了现有的问题,包括几个字段的最新/最低单行,或单个字段上的最新n行,但不是两个在一起

I need to construct 2 queries, using this example table (similar to this question ) 我需要构建2个查询,使用此示例表(类似于此问题

CREATE TABLE lap_data (
     id            int(1)     NOT NULL,
     track_id      int(1)     NOT NULL,
     user_id       int(1)     NOT NULL,
     lap_time      time       NOT NULL,
     lap_status    tinyint(1) NOT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

INSERT INTO `lap_data` (`id`, `track_id`, `user_id`, `lap_time`, `lap_status`) VALUES
(1, 1, 1, '04:18:23', 1),
(2, 2, 2, '01:09:54', 1),
(3, 2, 1, '01:05:30', 1),
(4, 1, 2, '04:17:02', 1),
(5, 3, 2, '01:13:10', 1),
(6, 4, 1, '01:36:59', 0),
(7, 3, 2, '01:18:10', 1),
(8, 2, 3, '01:06:42', 1),
(9, 1, 1, '04:16:12', 1),
(10, 1, 2, '04:18:12', 1),
(11, 2, 3, '01:03:20', 1),
(12, 2, 1, '01:08:13', 1),
(13, 2, 1, '01:09:44', 1),
(14, 3, 2, '01:14:10', 1),
(15, 3, 2, '01:17:20', 1),
(16, 4, 1, '01:36:23', 1),
(17, 2, 1, '01:11:34', 1);

Query 1: 查询1:

  • I want the top 2 results by lap_time - per track, per user 我想通过lap_time获得前2个结果 - 每个用户每个音轨
    • so if a given user_id has 20 records spread over 3 tracks... 所以如果给定的user_id有超过3个轨道的20条记录......
      • I would expect 6 results for that user_id 我期望该user_id有6个结果
      • or 5 if one of those tracks only has a single record 或者如果其中一首曲目只有一条记录
    • the same lap time at 2 different tracks should both be displayed, not appear just once 两个不同轨道的相同单圈时间都应该显示,而不是只出现一次
  • I want to use a WHERE user_id IN (..) for part 1 我想在第1部分中使用WHERE user_id IN(..)
  • ordered by a given field, ie lap_time 按给定字段排序,即lap_time
  • where lap_status = 1 其中lap_status = 1

This is the query I've been using elsewhere to collect the two latest records of a given type, which is the closest to what I think I need, modified to this example .. although unfinished, as in the other instance, it was only collecting the 2 most recent rows for each user_id .. here I need it to take track_id into account as well 这是我在其他地方用来收集给定类型的两个最新记录的查询,这个记录最接近我认为我需要的,修改为这个例子..虽然未完成,但在另一个例子中,它只是收集每个user_id的2个最新行..在这里我需要它也考虑到track_id

 SELECT
 * 
 FROM (
     SELECT
     t.id,
     t.track_id,
     t.user_id,
     t.lap_time,
     t.lap_status,
     @row:=case WHEN @prev=user_id THEN @row ELSE 0 END +1 rn,
     @prev:=user_id
     FROM `lap_data` t
     CROSS JOIN (SELECT @row:=0, @prev:=null) c
     WHERE t.user_id IN (1,2)
     ORDER BY user_id, id DESC
 ) src
 WHERE rn <= 2
 ORDER BY lap_time DESC

Query 1 Desired result: ( WHERE user_id IN (1,2) ) 查询1所需结果: (WHERE user_id IN(1,2))

|  id  |  track_id  |  user_id  |  lap_time  |  lap_status  |
--------------------------------------------------------------
|   9  |         1  |        1  |  04:16:12  |           1  |
|   1  |         1  |        1  |  04:18:23  |           1  |
|   3  |         2  |        1  |  01:05:30  |           1  |
|  12  |         2  |        1  |  01:08:13  |           1  |
|  16  |         4  |        1  |  01:36:23  |           1  |
|   6  |         4  |        1  |  01:36:59  |           1  |
|   4  |         1  |        2  |  04:17:02  |           1  |
|  10  |         1  |        2  |  04:18:12  |           1  |
|   2  |         2  |        2  |  01:09:54  |           1  |
|   5  |         3  |        2  |  01:13:10  |           1  |
|  14  |         3  |        2  |  01:14:10  |           1  |

Query 2: 查询2:

  • I want to get the inverse of the above, for a single user 对于单个用户,我想获得上述的反转
    • ie the remainder of the results excluding the first two, for just a specific user_id 即结果的其余部分,不包括前两个,仅针对特定的user_id

Query 2 Desired result: ( WHERE user_id = '1' ) 查询2所需结果: (WHERE user_id ='1')

|  id  |  track_id  |  user_id  |  lap_time  |  lap_status  |
--------------------------------------------------------------
|  13  |         2  |        1  |  01:09:44  |           1  |
|  17  |         2  |        1  |  01:11:34  |           1  |

Any suggestions? 有什么建议么? thanks! 谢谢!

MYSQL Version for my previous answer: 我之前回答的MYSQL版本:

SELECT 
lt.id,
lt.user_id,
lt.track_id,
lt.lap_status,
lt.lap_time 
FROM (
SELECT id,
    user_id,
    track_id,
    lap_status,
    lap_time,
    CASE WHEN track_id = @prevTrackId AND user_id = @prevUserId THEN @curRank := @curRank + 1 ELSE @curRank := 1 END Rank,
    @prevTrackId := track_id,
    @prevUserId := user_id
FROM lap_data, (SELECT @curRank := 0, @prevTrackId := 1, @prevUserId := 1) r
ORDER BY user_id,track_id,lap_time ASC
) lt 
WHERE Rank <= 2

This query makes one important (and quite likely erroneous) assumption - which is that the highest track_id of the previous user is higher than (or at least different from) the lowest track_id of the following user. 该查询产生一个重要的(并且很可能是错误的)假设 - 即先前用户的最高track_id高于(或至少不同)后续用户的最低track_id。

If that's not the case, then you may need to store another variable, or base 'prev' on the concatenation of two values... 如果不是这种情况,那么您可能需要存储另一个变量,或者将“prev”基于两个值的串联...

 SELECT x.id
      , x.track_id 
      , x.user_id 
      , x.lap_time 
      , x.lap_status 
   FROM 
      ( SELECT l.*
             , CASE WHEN @prev = track_id THEN @i:=@i+1 ELSE @i:=1 END rank
             , @prev:=track_id prev 
          FROM lap_data l
             , (SELECT @prev:=null,@i:=0) vars 
         WHERE user_id IN(1,2) 
         ORDER 
            BY user_id
             , track_id
             , lap_time
      ) x
  WHERE x.rank <=2
  ORDER 
     BY user_id
      , track_id
      , lap_time;
select id,track_id,user_id,lap_status,lap_time from (
select @rank:=if(@prev_cat=user_id,@rank+1,1) as rank,id,user_id,track_id,lap_status,lap_time,@prev_cat:=user_id
from lap_data,(select @rank:=0, @prev_cat:="")t
where user_id IN(1,2) order by track_id, user_id desc
  ) temp
  where temp.rank<=2

Query 1: 查询1:

SELECT lt.id,lt.user_id,lt.track_id,lt.lap_status,lt.lap_time 
FROM (
    SELECT id,user_id,track_id,lap_status,lap_time,Rank() 
    over (Partition BY user_id,track_id
    ORDER BY lap_time ASC ) AS Rank
    FROM lap_data
    WHERE user_id IN (1,2)
) lt WHERE Rank <= 2

Query 2: Simply just select all records which NOT IN Query 1. (assuming id is the primary key of your table, which you did not even set a primary key for lap_data table) 查询2:只需选择NOT IN Query 1中的所有记录(假设id是表的主键,你甚至没有为lap_data表设置主键)

SELECT * FROM lap_data
    WHERE id NOT IN
    (SELECT lt.id 
    FROM (
        SELECT id,Rank() 
        over (Partition BY user_id,track_id
        ORDER BY lap_time ASC ) AS Rank
        FROM lap_data
        WHERE user_id = 1
    ) lt WHERE Rank <= 2) 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM