150

我有一个用户签入和签出时间的表(“lms_attendance”),如下所示:

id  user    time    io (enum)
1   9   1370931202  out
2   9   1370931664  out
3   6   1370932128  out
4   12  1370932128  out
5   12  1370933037  in

我正在尝试创建此表的视图,该视图将仅输出每个用户 ID 的最新记录,同时为我提供“输入”或“输出”值,例如:

id  user    time    io
2   9   1370931664  out
3   6   1370932128  out
5   12  1370933037  in

到目前为止我已经很接近了,但我意识到视图不会接受子查询,这使得它变得更加困难。我得到的最接近的查询是:

select 
    `lms_attendance`.`id` AS `id`,
    `lms_attendance`.`user` AS `user`,
    max(`lms_attendance`.`time`) AS `time`,
    `lms_attendance`.`io` AS `io` 
from `lms_attendance` 
group by 
    `lms_attendance`.`user`, 
    `lms_attendance`.`io`

但我得到的是:

id  user    time    io
3   6   1370932128  out
1   9   1370931664  out
5   12  1370933037  in
4   12  1370932128  out

这很接近,但并不完美。我知道最后一个 group by 不应该在那里,但是没有它,它会返回最近的时间,而不是它的相对 IO 值。

有任何想法吗?谢谢!

4

13 回答 13

239

询问:

SQLFIDDLE示例

SELECT t1.*
FROM lms_attendance t1
WHERE t1.time = (SELECT MAX(t2.time)
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user)

结果:

| ID | USER |       TIME |  IO |
--------------------------------
|  2 |    9 | 1370931664 | out |
|  3 |    6 | 1370932128 | out |
|  5 |   12 | 1370933037 |  in |

请注意,如果用户有多个具有相同“最大”时间的记录,则上述查询将返回多条记录。如果您只希望每个用户有 1 条记录,请使用以下查询:

SQLFIDDLE示例

SELECT t1.*
FROM lms_attendance t1
WHERE t1.id = (SELECT t2.id
                 FROM lms_attendance t2
                 WHERE t2.user = t1.user            
                 ORDER BY t2.id DESC
                 LIMIT 1)
于 2013-06-11T07:22:09.080 回答
86

无需尝试重​​新发明轮子,因为这是常见的每组最大 n 问题。提出了非常好的解决方案

我更喜欢没有子查询的最简单的解决方案(参见 SQLFiddle,更新 Justin's)(因此易于在视图中使用):

SELECT t1.*
FROM lms_attendance AS t1
LEFT OUTER JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND (t1.time < t2.time 
         OR (t1.time = t2.time AND t1.Id < t2.Id))
WHERE t2.user IS NULL

这也适用于同一组中有两个具有相同最大值的不同记录的情况 - 这要归功于 with 的技巧(t1.time = t2.time AND t1.Id < t2.Id)。我在这里所做的只是确保如果同一用户的两条记录具有相同的时间,则只选择一条。Id标准是或其他东西实际上并不重要- 基本上任何保证唯一的标准都可以在这里完成工作。

于 2013-06-11T07:27:23.210 回答
6

基于@TMS 的回答,我喜欢它,因为不需要子查询,但我认为省略该'OR'部分就足够了,而且更容易理解和阅读。

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL

如果您对具有空时间的行不感兴趣,可以在WHERE子句中过滤它们:

SELECT t1.*
FROM lms_attendance AS t1
LEFT JOIN lms_attendance AS t2
  ON t1.user = t2.user 
        AND t1.time < t2.time
WHERE t2.user IS NULL and t1.time IS NOT NULL
于 2016-03-11T18:07:53.847 回答
5

已经解决了,但只是为了记录,另一种方法是创建两个视图......

CREATE TABLE lms_attendance
(id int, user int, time int, io varchar(3));

CREATE VIEW latest_all AS
SELECT la.user, max(la.time) time
FROM lms_attendance la 
GROUP BY la.user;

CREATE VIEW latest_io AS
SELECT la.* 
FROM lms_attendance la
JOIN latest_all lall 
    ON lall.user = la.user
    AND lall.time = la.time;

INSERT INTO lms_attendance 
VALUES
(1, 9, 1370931202, 'out'),
(2, 9, 1370931664, 'out'),
(3, 6, 1370932128, 'out'),
(4, 12, 1370932128, 'out'),
(5, 12, 1370933037, 'in');

SELECT * FROM latest_io;

单击此处在 SQL Fiddle 上查看它的实际效果

于 2013-06-11T07:30:50.030 回答
5

如果您在 MySQL 8.0 或更高版本上,您可以使用Window 函数

询问:

DBFiddle示例

SELECT DISTINCT
FIRST_VALUE(ID) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS ID,
FIRST_VALUE(USER) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS USER,
FIRST_VALUE(TIME) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS TIME,
FIRST_VALUE(IO) OVER (PARTITION BY lms_attendance.USER ORDER BY lms_attendance.TIME DESC) AS IO
FROM lms_attendance;

结果:

| ID | USER |       TIME |  IO |
--------------------------------
|  2 |    9 | 1370931664 | out |
|  3 |    6 | 1370932128 | out |
|  5 |   12 | 1370933037 |  in |

我看到使用Justin 提出的解决方案的优势在于,它使您能够从子查询中选择具有每个用户(或每个 id 或任何其他)最新数据的行,而无需中间视图或表。

如果您运行 HANA,它也会快 7 倍:D

于 2020-03-02T13:35:43.007 回答
0
select b.* from 

    (select 
        `lms_attendance`.`user` AS `user`,
        max(`lms_attendance`.`time`) AS `time`
    from `lms_attendance` 
    group by 
        `lms_attendance`.`user`) a

join

    (select * 
    from `lms_attendance` ) b

on a.user = b.user
and a.time = b.time
于 2013-06-11T07:18:18.573 回答
0

我尝试了一种适合我的解决方案

    SELECT user, MAX(TIME) as time
      FROM lms_attendance
      GROUP by user
      HAVING MAX(time)
于 2021-12-22T11:27:37.233 回答
0

好的,这可能是黑客攻击或容易出错,但不知何故这也有效 -

SELECT id, MAX(user) as user, MAX(time) as time, MAX(io) as io FROM lms_attendance GROUP BY id;
于 2019-06-26T15:09:37.953 回答
-1

我做了同样的事情,如下所示

SELECT t1.* FROM lms_attendance t1 WHERE t1.id in (SELECT max(t2.id) as id FROM lms_attendance t2 group BY t2.user)

这也将降低内存利用率。

谢谢。

于 2020-12-04T10:18:54.783 回答
-1
 select result from (
     select vorsteuerid as result, count(*) as anzahl from kreditorenrechnung where kundeid = 7148
     group by vorsteuerid
 ) a order by anzahl desc limit 0,1
于 2018-11-04T18:24:53.797 回答
-3

可能您可以按用户分组,然后按时间顺序排序。如下所示

  SELECT * FROM lms_attendance group by user order by time desc;
于 2013-06-11T07:17:03.273 回答
-3

这对我有用:

SELECT user, time FROM 
(
    SELECT user, time FROM lms_attendance --where clause
) AS T 
WHERE (SELECT COUNT(0) FROM table WHERE user = T.user AND time > T.time) = 0
ORDER BY user ASC, time DESC
于 2017-12-13T21:59:37.130 回答
-3

试试这个查询:

  select id,user, max(time), io 
  FROM lms_attendance group by user;
于 2017-06-30T07:57:10.873 回答