7

在 MySQL 中,如何选择每一行都满足特定条件的数据?例如,假设我有一个表格显示员工何时上班,它包含三个字段:

CREATE TABLE ArrivalTimes
(UserID INT
,Day DATE 
,ArrivalTime TIME
);

我想选择从未迟到(早上 9 点或更早到达)的所有员工的用户 ID,最好的方法是什么?

4

5 回答 5

19

@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并说:

  • a1:“ Using index; Using temporary
  • a2:“ 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'); 
  • a1:“ Using where; Using index
  • a2:“ Using where

这似乎最有可能具有最佳性能。诚然,我的测试表中只有四行,所以这不是一个有代表性的测试。

于 2009-02-11T19:21:04.533 回答
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 的答案。

于 2009-02-11T19:18:03.730 回答
1
SELECT userID, MAX(ArrivalTime) as latest
FROM ArrivalTimes 
WHERE latest <= '9:00:00'
GROUP BY userID
于 2009-02-11T19:19:01.903 回答
1

比尔·卡尔文建议:

试试这个:

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'失败。)

于 2009-04-28T11:06:06.013 回答
1

您可以通过 3 种方法获取更多结果 1.使用 Group-By 功能 2.使用子查询 3.使用连接......等

使用组 - 按

SELECT userID, MAX(ArrivalTime) as latest FROM ArrivalTimes WHERE latest <= '9:00:00'

按用户 ID 分组

使用子查询

select * from user a where '09:00:00'

= all(从 ArrivalTime b 中选择 ArrivalTime,其中 b.UserID = a.ID);


你也可以使用自我内部连接来获得它

于 2014-02-04T12:29:54.293 回答