如果您只想返回一行,最简单的方法是:
SELECT t.*
FROM table_name t
WHERE t.name = '$username'
AND t.theDate >= CAST(DATE_FORMAT(NOW(),'%Y-%m-01') AS DATE)
AND t.theDate < DATE_ADD(DATE_FORMAT(NOW(),'%Y-%m-01'), INTERVAL 1 MONTH)
ORDER BY s.name DESC, s.theDate DESC
LIMIT 1
ORDER BY 假设您在(name,theDate) 上(或前导列)有一个索引。这将是谓词的最合适的索引(即 WHERE 子句中的条件。我们真的不需要对 name 列进行排序,因为我们知道它将等于某个东西......但是指定 ORDER BY这样一来,MySQL 更有可能对索引进行反向扫描操作,以正确的顺序返回行,避免文件排序操作。
注意:我在 WHERE 子句的条件中指定了 theDate 裸列,而不是将其包装在任何函数中...通过指定裸列和有界范围,我们使 MySQL 能够使用索引范围扫描操作。还有其他可能的方法可以在 WHERE 子句中包含此条件,例如...
DATE_FORMAT(t.theDate,'%Y-%m') = DATE_FORMAT(NOW(),'%Y-%m')
这将返回一个等价的结果,但是这样的谓词是不可分割的。也就是说,MySQL 不能/不会对索引进行范围扫描来满足这一点。
如果您打算为给定用户获取一个月内“最少”日期的所有行(您的问题似乎并不表明您只需要一行),这是获得该结果的一种方法:
SELECT t.*
FROM table_name t
JOIN ( SELECT s.name
, s.theDate
FROM table_name s
WHERE s.name = '$username'
AND s.theDate >= CAST(DATE_FORMAT(NOW(),'%Y-%m-01') AS DATE)
AND s.theDate < DATE_ADD(DATE_FORMAT(NOW(),'%Y-%m-01'), INTERVAL 1 MONTH)
ORDER BY s.name DESC, s.theDate DESC
LIMIT 1
) r
ON r.name = t.name
AND r.theDate = t.theDate
再一次,MySQL 可以使用带有前导列 (name,theDate) 的索引(如果可用)来满足谓词,并执行反向扫描操作(避免排序)和执行 JOIN 操作。
注意:我们在这里假设“theDate”是数据类型 DATE(没有时间组件)。如果它是 DATETIME 或 TIMESTAMP,则可能存在时间分量,并且如果具有相同“日期”的行的时间分量不同,则该查询可能不会返回给定“日期”值的所有行。 (例如,'2012-07-13 17:30' 和 '2012-07-13 19:55' 是不同的日期时间值。)如果我们想要返回这两行(因为两者都是“7 月 13 日”的日期) ,我们需要进行范围扫描而不是相等测试。
SELECT t.*
FROM table_name t
JOIN ( SELECT s.name
, s.theDate
FROM table_name s
WHERE s.name = '$username'
AND s.theDate >= CAST(DATE_FORMAT(NOW(),'%Y-%m-01') AS DATE)
AND s.theDate < DATE_ADD(DATE_FORMAT(NOW(),'%Y-%m-01'), INTERVAL 1 MONTH)
ORDER BY s.name DESC, s.theDate DESC
LIMIT 1
) r
ON t.name = r.name
AND t.theDate >= r.theDate
AND t.theDate < DATE_FORMAT(DATE_ADD(r.theDate,INTERVAL 1 DAY),'%Y-%m-%d')
请注意最后两行...我们正在寻找任何具有theDate 值的行,该值大于或等于为当前月份找到的“最小”值,并且还小于该月的午夜次日。