MySQL - 选择当前月/年的最小日期,不一定是该月的第一天

时间:2012-07-14 02:24:03

标签: php mysql database

使用PHP / MySQL

我试图创建一个select语句,从当前月份和年份的最少日期获取数据(我用它来显示某个玩家的数据'本月& #39)。月份中的第一个数据可能并不总是存在,因此如果第一个数据未找到,那么它将使用第二个或第三个等等,以最少月份为准。

查询需要类似于:

SELECT * FROM table_name WHERE name = '$username' AND
[...theDate = the least day of data in the current month/year]

它会返回一行数据。

这是我用来获取本周'的一个查询。数据:

SELECT name, data, theDate
FROM table_name
WHERE name = '$username'
AND YEARWEEK(theDate) = YEARWEEK(CURRENT_DATE)
ORDER BY theDate;

2 个答案:

答案 0 :(得分:2)

这是一个简单的解决方案:

SELECT a.*
FROM table_name a
INNER JOIN
(
    SELECT name, MIN(theDate) AS mindate
    FROM table_name
    WHERE MONTH(theDate) = MONTH(CURDATE()) AND
          YEAR(theDate) = YEAR(CURDATE()) AND
          name = '$username'
    GROUP BY name
) b ON a.name = b.name AND a.theDate = b.mindate
ORDER BY theDate

内部子选择首先选择当前月份和当前年份的最小日期。这应该只返回一个结果。

外部查询然后加入这一个结果,从而得到当月最早记录日的行&年。

假设每个玩家每天只生成一行,我们可以进一步简化:

SELECT *
FROM table_name
WHERE MONTH(theDate) = MONTH(CURDATE()) AND
      YEAR(theDate) = YEAR(CURDATE()) AND
      name = '$username'
ORDER BY theDate
LIMIT 1

答案 1 :(得分:1)

如果您只想返回一行,最简单的方法是:

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基于您({或1}}的前导列上有索引的假设。这将是谓词最合适的索引(即WHERE子句中的条件。我们真的没有必要对(name,theDate)列进行排序,因为我们知道它将等于某些东西......但是指定了ORDER BY以这种方式使得MySQL更有可能对索引执行反向扫描操作,以正确的顺序返回行,从而避免文件输出操作。

注意:我在WHERE子句的条件中指定了裸name列,而不是将其包装在任何函数中......通过指定裸列和有界范围,我们使MySQL能够使用索引范围扫描操作。还有其他可能的方法在WHERE子句中包含此条件,例如......

theDate

将返回相同的结果,但像这样的谓词不是可以理解的。也就是说,MySQL不能/不会对索引进行范围扫描以满足这一要求。

如果您打算在一个月内为给定用户获取“最少”日期的所有行(您的问题似乎并不表示您只需要一行),这是获得该结果的一种方式:< / p>

DATE_FORMAT(t.theDate,'%Y-%m') = DATE_FORMAT(NOW(),'%Y-%m')

同样,MySQL可以使用带有前导列(name,theDate)的索引(如果可用)来满足谓词,并执行反向扫描操作(避免排序),并执行JOIN操作。 / p>

注意:我们假设'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 r.name = t.name
   AND r.theDate = t.theDate 

请注意最后两行...我们正在查找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') 值大于或等于当前月份的“最小”值并且小于午夜的所有行第二天。