如何正确编写SQL统计到期日期为今日的数据库记录数?
嘿,我来帮你搞定这两个到期记录统计的需求,尤其是你那个跑不通的SQL语句~
首先明确下,你的两个需求其实指向同一个目标:统计数据库中到期日期恰好与今日匹配的所有记录数。接下来咱们重点排查你遇到的SQL问题,同时分场景给出最优写法。
你的原语句SELECT COUNT (id) FROM tasks WHERE due_date = CURDATE无法正常运行,大概率是这两个原因:
- CURDATE函数忘记加括号:在MySQL、MariaDB这类常用数据库里,
CURDATE()是需要带括号调用的函数,不带括号会被数据库误识别成列名,自然会报错。 - 字段类型不匹配导致漏统计:如果你的
due_date字段是DATETIME或TIMESTAMP类型(包含时分秒信息),而CURDATE()返回的是纯日期(比如2024-05-20),直接用等号匹配的话,那些带具体时间的记录(比如2024-05-20 14:30:00)会被漏掉,因为完整的datetime值不等于纯日期。
下面分场景给你正确的SQL写法:
场景1:due_date是DATE类型(仅存储日期,无时间)
这种情况最直接,补上CURDATE()的括号即可,另外推荐用COUNT(*)代替COUNT(id)(如果id不为空的话两者效果一样,但COUNT(*)性能更优,是统计行数的标准写法):
SELECT COUNT(*) FROM tasks WHERE due_date = CURDATE();
场景2:due_date是DATETIME/TIMESTAMP类型(包含时分秒)
这里有两种靠谱的写法:
写法1:转换字段为纯日期匹配
用DATE()函数把due_date转换成纯日期后再和今日日期对比:
SELECT COUNT(*) FROM tasks WHERE DATE(due_date) = CURDATE();
优点是写法直观,缺点是如果due_date上有索引的话,这个转换会导致索引失效,数据量大时查询变慢。
写法2:用日期范围匹配(推荐)
通过范围查询匹配今日0点到明日0点前的所有记录,这种写法可以利用due_date上的索引,性能更好:
SELECT COUNT(*) FROM tasks WHERE due_date >= CURDATE() AND due_date < DATE_ADD(CURDATE(), INTERVAL 1 DAY);
解释一下:CURDATE()返回今日的00:00:00,DATE_ADD(CURDATE(), INTERVAL 1 DAY)返回明日的00:00:00,所以这个条件会匹配所有今日产生的datetime记录,不会遗漏也不会多统计。
如果是其他数据库(比如PostgreSQL),对应的日期函数是CURRENT_DATE,写法会是SELECT COUNT(*) FROM tasks WHERE due_date = CURRENT_DATE;,不过从你用CURDATE来看,应该是MySQL系的数据库,上面的写法就够用啦。
内容的提问来源于stack exchange,提问作者Algirdas Žarkaitis

