在MySQL中,如何选择每行满足特定条件的数据?例如,假设我有一张表格显示员工何时到达工作岗位,它有三个字段:
CREATE TABLE ArrivalTimes
(UserID INT
,Day DATE
,ArrivalTime TIME
);
我想选择所有从未迟到的员工的用户ID(早上9点或更早到达),最好的方法是什么?
答案 0 :(得分:17)
@jjclarkson和@ davethegr8的答案很接近,但是你不能把聚合函数放在WHERE子句中。将为每一行评估WHERE子句。
您需要评估每个组的MAX()
表达式,因此您需要使用HAVING
子句。
试试这个:
SELECT UserID
FROM ArrivalTimes
GROUP BY UserID
HAVING MAX(ArrivalTime) <= '09:00:00';
@MBCook评论HAVING
可能很慢。你是对的,它可能不是产生预期结果的最快捷方式。但HAVING
解决方案是 clear 。在某些情况下,性能的优先级低于清晰度和可维护性。
我查看了HAVING
解决方案的EXPLAIN输出(在MySQL 5.1.30上):没有使用索引,额外的注释说“Using temporary; Using filesort
”,这通常意味着性能不佳。
考虑以下问题:
SELECT DISTINCT a1.UserID
FROM ArrivalTimes a1
LEFT OUTER JOIN ArrivalTimes a2
ON (a1.UserID = a2.UserID AND a2.ArrivalTime > '09:00:00')
WHERE a2.UserID IS NULL;
这会生成一个优化计划,该计划使用UserID
上的索引并说:
Using index; Using temporary
”Using where; Distinct
”最后,以下查询生成一个优化计划,该计划似乎最有效地使用索引,并且没有临时表或文件排序。
SELECT DISTINCT a1.UserID
FROM ArrivalTimes a1
WHERE NOT EXISTS (SELECT * FROM ArrivalTimes a2
WHERE a1.UserID = a2.UserID
AND a2.ArrivalTime > '09:00:00');
Using where; Using index
”Using where
”这似乎最有可能获得最佳表现。不可否认,我的测试表中只有四行,所以这不是代表性的测试。
答案 1 :(得分:1)
这是一个很好的想法,但它不起作用。
SELECT UserID FROM ArrivalTimes WHERE MAX(ArrivalTime) <= '09:00:00' GROUP BY UserID
使用此查询,您将收到错误消息:“无效使用组功能”
根据定义,COUNT,MAX,MIN,AVG,SUM等聚合函数在一组(或一组记录)上执行它们的功能,因此MAX(ArrivalTime)需要采用以下形式:
GROUP BY UserID HAVING MAX(ArrivalTime) <= '09:00:00'
见上面@Bill Karwin的答案。
答案 2 :(得分:1)
SELECT userID, MAX(ArrivalTime) as latest
FROM ArrivalTimes
WHERE latest <= '9:00:00'
GROUP BY userID
答案 3 :(得分:1)
Bill Karwin建议:
试试这个:
SELECT UserID
FROM ArrivalTimes
GROUP BY UserID
HAVING MAX(ArrivalTime) <= '09:00:00';
我查看了用于HAVING解决方案的EXPLAIN输出(在MySQL 5.1.30上):没有使用索引,额外的注释说“使用临时;使用filesort”,这通常意味着性能很差。
我认为以下内容更清楚,因为有一个用户表,ArrivalTimes.UserId是一个外键。这将选择所有从不迟到的用户:
select * from user a
where '09:00:00'
>= all( select ArrivalTime from ArrivalTime b where b.UserID = a.ID);
选择任何迟到的用户:
select * from user a
where '09:00:00'
< any( select ArrivalTime from ArrivalTime b where b.UserID = a.ID);
这更清楚,因为它更符合我们的英语/自然语言规范。
它避免了group by
的低效率;在MySql 5.0.51下,它不需要临时或文件输出,就像Bill的那样。
(请注意,它确实需要将常量时间值置零,因此:'09:00:00'
; '9:00:00'
失败。)
答案 4 :(得分:1)
您可以为此查询获取更多3种方法的结果 1.使用分组功能 2.使用子查询 3.使用加入......等等。
SELECT userID,MAX(ArrivalTime)为最新版本 来自ArrivalTimes 最新的&lt; ='9:00:00'
从用户a中选择* '09:00:00'
= all(从ArrivalTime b中选择ArrivalTime,其中b.UserID = a.ID);
你也可以使用自我内部联接来获取它