使用MIN获取对应的行值到GROUP BY

时间:2017-11-06 21:30:16

标签: mysql group-by

我的数据是这样的:

+------------+---------+-------+
|    Name    |  Time   | Flag  |
+------------+---------+-------+
| Bob        | 401     | 1     |
| Bob        | 204     | 0     |
| Dan        | 402     | 1     |
| Dan        | 210     | 0     |
| Jeff       | 204     | 0     |
| Fred       | 407     | 1     |
| Mike       | 415     | 1     |
| Mike       | 238     | 0     |
+------------+---------+-------+

我想得到每个人的最佳时间,但如果设置了“旗帜”,他们的时间应该除以2。 例如,Bob的最佳时间是200.5

现在我可以做一个相对简单的查询来获取这样的数据:

SELECT userid,
       MIN(CASE WHEN flag = 1 THEN time / 2 ELSE time END) AS convertedTime,
       time,
       flag
FROM   times t
GROUP  BY userid
ORDER  BY convertedtime ASC

这里的问题是没有为时间和标志返回正确的相应数据,得到这些数据:

Bob     200.5   204     0

而不是正确的数据

Bob     200.5   401     1

当然,我看到了上一个查询的问题,我用它修复了它:

 SELECT userid,
       MIN(convertedtime) AS convertedTime,
       (SELECT time
        FROM   times
        WHERE  MIN(convertedtime) = CASE
                                      WHEN flag = 1 THEN time / 2
                                      ELSE time
                                    end
        LIMIT  1)         AS time,
       (SELECT flag
        FROM   times
        WHERE  MIN(convertedtime) = CASE
                                      WHEN flag = 1 THEN time / 2
                                      ELSE time
                                    end
        LIMIT  1)         AS flag
FROM   (SELECT userid,
               CASE
                 WHEN flag = 1 THEN time / 2
                 ELSE time
               end AS convertedTime,
               time,
               flag
        FROM   times t) AS t
GROUP  BY userid
ORDER  BY convertedtime ASC

SQLFiddle

这确实有效,但我觉得必须有一种更好,更有效的方法。在我的实际查询中,我将时间除以2的部分是一个更长的公式,我有数千行,所以它非常慢。

所以问题是,对此有更好/更有效的查询吗?

2 个答案:

答案 0 :(得分:1)

使用SQL select only rows with max value on a column中的常用技巧,但在比较flag条件中的值时添加ON检查。

SELECT t1.username, t2.convertedtime, t1.time, t1.flag
FROM times AS t1
JOIN (SELECT username,
             MIN(IF(flag = 1, time/2, time) AS convertedtime
      FROM times
      GROUP BY username) AS t2
ON t1.username = t2.username
AND t1.time = IF(t1.flag = 1, t2.convertedtime * 2, t2.convertedtime)

这可能不会非常有效 - 我不认为它可以使用索引进行条件比较。

答案 1 :(得分:0)

这是我最终使用的(在这种情况下,由于我的更复杂的实际查询在其中进行舍入,因此无法乘以2来恢复数字),但Barmar与我做了几乎相同的事情&# 39;这样做我把他标记为答案。

SELECT times.userID, times2.convertedTime, times.time, times.flag
FROM times JOIN
(
SELECT userid,
       MIN(CASE WHEN flag = 1 THEN time / 2 ELSE time END) AS convertedTime,
       time,
       flag
FROM   times t
GROUP  BY userid
ORDER  BY convertedtime ASC
) as times2 ON times.userid = times2.userid AND 
(
  (times.time / 2 = times2.convertedTime AND times.flag = 1) OR 
  (times.time = times2.convertedTime AND times.flag = 0)
)