不匹配的MySQL日期被判定为相等问题及查询求助
首先,我先把你的查询贴出来方便分析:
select w.EventName, w.EventLocation, CONCAT(CURDATE(), ' ', w.RecurringEventTime) AS RecurringEventTime, w.OneTimeDateTime from EventClickIns eci join WebEvents w on eci.WebEventID = w.ID where eci.UserID = 493 and eci.WebEventID <> 10 and eci.InvitationID <> 175 and date(eci.ClickInDate) = date(now()) and (RecurringEventTime = '2018-03-19 12:00:00' OR w.OneTimeDateTime = '2018-03-19 12:00:00') limit 1
我之前踩过类似的日期匹配坑,结合你的情况,主要有几个可能的原因和对应的解决办法:
一、最可能的原因:WHERE子句错误引用SELECT别名
MySQL的查询执行顺序是:FROM/JOIN → WHERE → SELECT → ORDER BY/LIMIT。也就是说,WHERE阶段在SELECT之前执行,这时候你在SELECT里定义的RecurringEventTime别名还没有被解析。
如果你的WebEvents表恰好存在一个名为RecurringEventTime的实际列,MySQL会直接用这个表列的值来做比较,而不是你用CONCAT(CURDATE(), ' ', w.RecurringEventTime)拼接出来的那个值。这就会导致明明你期望的是拼接后的时间匹配,结果实际用了原表的时间列,自然会出现日期不匹配却被选中的异常。
解决办法:
把WHERE里的别名替换成实际的拼接表达式,或者用子查询/CTE先计算出别名列再过滤:
方案1:直接替换表达式
select w.EventName, w.EventLocation, CONCAT(CURDATE(), ' ', w.RecurringEventTime) AS RecurringEventTime, w.OneTimeDateTime from EventClickIns eci join WebEvents w on eci.WebEventID = w.ID where eci.UserID = 493 and eci.WebEventID <> 10 and eci.InvitationID <> 175 and date(eci.ClickInDate) = date(now()) -- 把别名替换成实际的拼接表达式 and (CONCAT(CURDATE(), ' ', w.RecurringEventTime) = '2018-03-19 12:00:00' OR w.OneTimeDateTime = '2018-03-19 12:00:00') limit 1
方案2:用子查询先计算别名列
SELECT t.EventName, t.EventLocation, t.RecurringEventTime, t.OneTimeDateTime FROM ( SELECT w.EventName, w.EventLocation, CONCAT(CURDATE(), ' ', w.RecurringEventTime) AS RecurringEventTime, w.OneTimeDateTime, eci.UserID, eci.WebEventID, eci.InvitationID, eci.ClickInDate FROM EventClickIns eci JOIN WebEvents w ON eci.WebEventID = w.ID ) t WHERE t.UserID = 493 AND t.WebEventID <> 10 AND t.InvitationID <> 175 AND DATE(t.ClickInDate) = DATE(NOW()) AND (t.RecurringEventTime = '2018-03-19 12:00:00' OR t.OneTimeDateTime = '2018-03-19 12:00:00') LIMIT 1
二、次要原因:日期时间类型不匹配导致隐式转换错误
如果w.RecurringEventTime是VARCHAR类型而不是TIME类型,拼接的时候可能出现格式问题。比如如果存储的是'1200'而不是'12:00:00',拼接后会变成'2024-05-20 1200',和'2018-03-19 12:00:00'比较时,MySQL会做隐式转换,可能把字符串转成日期时出现意外匹配。
解决办法:
确保w.RecurringEventTime是TIME类型,如果暂时没法修改表结构,拼接前先把它转成TIME类型:
CONCAT(CURDATE(), ' ', CAST(w.RecurringEventTime AS TIME))
三、潜在原因:时区不一致导致日期错位
如果你的服务器时区和存储的OneTimeDateTime、RecurringEventTime的时区不一致,也会出现这种问题。比如OneTimeDateTime存储的是UTC时间,而CURDATE()返回的是服务器本地时区的日期,拼接后的时间和目标时间的时区不统一,就会出现匹配错误。
解决办法:
统一时区,比如如果存储的是UTC时间,就用UTC_DATE()代替CURDATE():
CONCAT(UTC_DATE(), ' ', w.RecurringEventTime)
同时,比较OneTimeDateTime时也要考虑时区转换,比如:
-- 把UTC时间转成服务器本地时区后再比较 CONVERT_TZ(w.OneTimeDateTime, '+00:00', @@session.time_zone) = '2018-03-19 12:00:00'
内容的提问来源于stack exchange,提问作者HerrimanCoder

