选择每位用户最近日期的行


125

我有一张用户签入和签出时间的表(“ 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的最新记录,同时为我提供“ in”或“ out”值,如下所示:

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

这很接近,但并不完美。我知道最后一个分组依据不应存在,但是没有它,它将返回最近的时间,但不会返回相对IO值。

有任何想法吗?谢谢!



返回手册。您将看到,无论有没有子查询(相关和不相关),它都能为该问题提供解决方案。
草莓2013年

@Barmar,从技术上讲,正如我在回答中指出的那样,这是所有700个问题的重复,每个问题的组数均为n
TMS

@Prodikl,什么是“ io(枚举)”?
莫妮卡·赫德内克

我有一列“ IO”代表“ in or out”,这是一个枚举类型,可能的值是“ in”或“ out”。这用于跟踪人们何时上班和下班。
基思

Answers:


199

查询:

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 |

每次都会起作用的解决方案:

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)

2
哇!不仅完成了这项工作,还允许我使用此查询创建视图,即使该查询包含子查询也是如此。以前,当我尝试创建包含子查询的视图时,它没有让我接受。是否有关于为什么允许这样做但没有另一个规则的规则?
Keith 2013年

很奇怪。万分感谢!也许是因为我的子查询是我选择FROM的伪表,在此示例中,它在WHERE子句中使用。
基思(Keith)

4
无需子查询!此外,如果存在两个完全相同的记录,则此解决方案不起作用。无需每次都尝试重新发明轮子,因为这是常见的问题-相反,请使用已经过测试和优化的解决方案-@Prodikl看到了我的答案。
TMS

啊,感谢您的见识!明天我在办公室时,我将尝试使用新密码。
Keith 2013年

3
@TMS如果记录具有完全相同的时间,则此解决方案有效,因为查询正在查找ID最大的记录。这意味着表中的时间就是插入时间,这可能不是一个很好的假设。相反,您的解决方案将比较时间戳,并且当两个时间戳相同时,您还将返回ID最大的行。因此,您的解决方案还假定此表中的时间戳与插入顺序有关,这是两个查询的最大缺陷。
WebWanderer

73

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

我更喜欢没有子查询(因此易于在视图中使用)的最简单的解决方案(请参阅SQLFiddle,已更新的贾斯汀的):

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

在同一组中有两个具有相同最大值的不同记录的情况下,这也可以使用-借助的技巧(t1.time = t2.time AND t1.Id < t2.Id)。我在这里所做的只是确保在同一用户的两条记录具有相同时间的情况下,仅选择一条。准则是否真的无关紧要Id-基本上任何保证唯一的准则都可以在这里完成工作。


1
最大使用量t1.time < t2.time和最小值将t1.time > t2.time与我最初的直觉相反。

1
@ J.Money,因为隐藏了隐式否定:在条件适用的情况下,您从t1中选择所有没有对应记录的记录t1.time < t2.time:-)
TMS

4
WHERE t2.user IS NULL有点奇怪 这条线起什么作用?
tumultous_rooster15年

1
贾斯汀(Justin)发布的已接受答案可能更为理想。可接受的答案是对表的主键使用向后索引扫描,然后是限制,然后是表的顺序扫描。因此,可以通过附加索引极大地优化接受的答案。该查询还可以通过索引进行优化,因为它执行两次序列扫描,但还包括哈希值和序列扫描结果的“哈希-反联接”以及其他序列扫描的哈希值。我将对哪种方法真正更理想的解释感兴趣。
WebWanderer

@TMS您能否说明清楚OR (t1.time = t2.time AND t1.Id < t2.Id))
奥列格·库茨

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

OR如果两个记录可以具有相同的内容,则忽略该部分是一个非常糟糕的主意time
TMS

为了性能起见,我会避免使用此解决方案。正如@OlegKuts提到的那样,这在中大型数据集上非常慢。
Peter Meadley '18

4

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

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中的运行情况


1
感谢您的跟进!是的,如果没有更简单的方法,我将创建多个视图。再次感谢
Keith

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

谢谢。我知道我可以使用子查询来做到这一点,但我希望将其转换为视图,并且不允许在视图AFAIK中进行子查询。我是否必须将每个子查询变成一个视图等?
基思(Keith)2013年

join (select * from lms_attendance ) b= join lms_attendance b
azerafati

0
 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

0

如果您使用的是MySQL 8.0或更高版本,则可以使用Window函数

查询:

DBFiddleExample

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


-1

好的,这可能是黑客入侵或容易出错,但无论如何它还是可以正常工作的-

SELECT id, MAX(user) as user, MAX(time) as time, MAX(io) as io FROM lms_attendance GROUP BY id;

-2

试试这个查询:

  select id,user, max(time), io 
  FROM lms_attendance group by user;

尝试为此做一个SQLFiddle。您可能会发现idio是非聚合列,不能在中使用group by
德维·摩根

1
无法保证id将是具有max(time)的id,它可以是组中的任何id。这是我来这里解决但仍在寻找的问题
robisrob

-3

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

  SELECT * FROM lms_attendance group by user order by time desc;

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.