在 MySQL 中,如何选择每一行都满足特定条件的数据?例如,假设我有一个表格显示员工何时上班,它包含三个字段:
CREATE TABLE ArrivalTimes
(UserID INT
,Day DATE
,ArrivalTime TIME
);
我想选择从未迟到(早上 9 点或更早到达)的所有员工的用户 ID,最好的方法是什么?
@jjclarkson 和 @davethegr8 的答案很接近,但您不能将聚合函数放在 WHERE 子句中。为每一行评估 WHERE 子句。
您需要评估MAX()
每个组的表达式,因此您需要使用HAVING
子句。
试试这个:
SELECT UserID
FROM ArrivalTimes
GROUP BY UserID
HAVING MAX(ArrivalTime) <= '09:00:00';
@MBCook 评论HAVING
可能很慢。你是对的,这可能不是产生所需结果的绝对最快的方法。但HAVING
解决办法是最清楚的。在某些情况下,性能的优先级低于清晰度和可维护性。
我查看了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
”这似乎最有可能具有最佳性能。诚然,我的测试表中只有四行,所以这不是一个有代表性的测试。
这是一个很好的想法,但它不起作用。
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 的答案。
SELECT userID, MAX(ArrivalTime) as latest
FROM ArrivalTimes
WHERE latest <= '9:00:00'
GROUP BY userID
比尔·卡尔文建议:
试试这个:
SELECT UserID
FROM ArrivalTimes
GROUP BY UserID
HAVING MAX(ArrivalTime) <= '09:00:00';
我查看了 HAVING 解决方案的 EXPLAIN 输出(在 MySQL 5.1.30 上):没有使用索引,并且额外的注释说“使用临时;使用文件排序”,这通常意味着性能会很差。
鉴于有一个 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);
这更清楚,因为它更符合我们的英语/自然语言规范。
它避免了 a 的低效率group by
;在 MySql 5.0.51 下,它不需要像 Bill 那样临时或文件排序。
(请注意,它确实需要将常数时间值补零,因此:'09:00:00'
;'9:00:00'
失败。)
您可以通过 3 种方法获取更多结果 1.使用 Group-By 功能 2.使用子查询 3.使用连接......等
SELECT userID, MAX(ArrivalTime) as latest FROM ArrivalTimes WHERE latest <= '9:00:00'
select * from user a where '09:00:00'
= all(从 ArrivalTime b 中选择 ArrivalTime,其中 b.UserID = a.ID);
你也可以使用自我内部连接来获得它