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

时间:2016-09-15 03:38:46

标签: mysql database

我发现现有问题包括几个字段的最新/最低单行,或单个字段的最新n行,但不是两个一起

我需要使用此示例表(类似于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);

查询1:

  • 我想通过lap_time获得前2个结果 - 每个用户每首曲目
    • 所以如果给定的user_id有20条记录分布在3条轨道上......
      • 我希望该user_id
      • 有6个结果
      • 或5,如果其中一首曲目只有一条记录
    • 两个不同曲目的同一圈时间都应该显示,而不是只出现一次
  • 我想在第1部分
  • 中使用WHERE user_id IN(..)
  • 按给定字段排序,即lap_time
  • 其中lap_status = 1

这是我在其他地方用来收集给定类型的两个最新记录的查询,这个记录最接近我认为我需要的,修改为这个例子..虽然未完成,但在另一个例子中,它只为每个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

查询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  |

查询2:

  • 我想得到一个用户的上述反转
    • 即结果的其余部分,不包括前两个,仅针对特定的user_id

查询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  |

有什么建议吗?谢谢!

4 个答案:

答案 0 :(得分:1)

我之前回答的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

答案 1 :(得分:0)

此查询产生一个重要的(很可能是错误的)假设 - 即前一个用户的最高track_id高于(或至少不同)以下用户的最低track_id。

如果情况并非如此,那么您可能需要存储另一个变量,或者基于&#39; prev&#39;关于两个值的串联......

 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;

答案 2 :(得分:0)

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

答案 3 :(得分:-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

查询2: 只需选择NOT IN查询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)