2

使用 PHP/MySQL

我正在尝试创建一个选择语句,该语句从当前月份和年份的最少一天获取数据(我正在使用它来显示来自某个玩家“本月”的数据)。第一个月份的数据可能并不总是存在,因此如果没有找到第一个,那么它将使用第二个或第三个等,以最小的月份为准。

查询需要类似于:

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;
4

2 回答 2

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
于 2012-07-14T02:48:47.927 回答
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 是基于您在(或具有前导列)上有索引的假设(name,theDate)。那将是谓词的最合适的索引(即 WHERE 子句中的条件。我们确实不需要对name列进行排序,因为我们知道它将等于某物……但是以这种方式指定 ORDER BY使 MySQL 更有可能对索引执行反向扫描操作,以正确的顺序返回行,避免文件排序操作。

注意:我theDate在 WHERE 子句的条件中指定了裸列,而不是将其包装在任何函数中……通过指定裸列和有界范围,我们使 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值大于或等于当前月份找到的“最小”值并且也小于第二天午夜的行。

于 2012-07-14T02:42:51.547 回答