MySQL中STR_TO_DATE在子查询结合临时表创建时失效的问题排查
我之前碰到过一模一样的情况!单独跑查询没问题,但套进临时表创建语句就报错,核心原因是SQL模式的严格性以及MySQL在不同执行语境下的检查逻辑差异:单独执行SELECT时,MySQL可能只会对无效日期字符串返回NULL并打个警告,但创建临时表时,严格SQL模式会强制让STR_TO_DATE直接抛出错误,导致整个语句失败。
问题原因拆解
你的子查询SELECT DB_ID FROM verkehrsweg v1 where STR_TO_DATE(v1.LASTUPDATEDBY, GET_FORMAT(DATE,'ISO')) is null中,当LASTUPDATEDBY是像'Hallo'这种不符合日期格式的字符串时,若会话的sql_mode包含STRICT_TRANS_TABLES或STRICT_ALL_TABLES(这是MySQL默认的严格模式配置),STR_TO_DATE不会返回NULL,而是直接抛出1411错误。单独执行SELECT时,可能因为查询优化器的处理逻辑不同,或者临时跳过了严格检查,所以没触发报错,但创建临时表时的元数据校验会严格执行规则。
可行解决方案
下面提供几种适配不同MySQL版本的解决方法,按推荐程度排序:
方法1:使用TRY_CAST(MySQL 8.0及以上版本)
MySQL 8.0引入的TRY_CAST函数专门解决这类转换失败报错的问题——它在转换失败时会返回NULL而不是抛出错误,完全适配你的场景:
DROP TEMPORARY TABLE IF EXISTS my_temp_id_table; CREATE TEMPORARY TABLE my_temp_id_table( SELECT DB_ID from verkehrsweg v where DB_ID in ( SELECT DB_ID FROM verkehrsweg v1 where TRY_CAST(v1.LASTUPDATEDBY AS DATE) IS NULL) and LASTUPDATEDBY is not null );
方法2:正则表达式前置过滤(兼容低版本MySQL)
如果你的MySQL版本低于8.0,可以先用正则表达式匹配ISO格式的日期(YYYY-MM-DD),只对符合格式的字符串调用STR_TO_DATE,避免无效值触发错误:
DROP TEMPORARY TABLE IF EXISTS my_temp_id_table; CREATE TEMPORARY TABLE my_temp_id_table( SELECT DB_ID from verkehrsweg v where DB_ID in ( SELECT DB_ID FROM verkehrsweg v1 WHERE (LASTUPDATEDBY NOT REGEXP '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' OR STR_TO_DATE(v1.LASTUPDATEDBY, GET_FORMAT(DATE,'ISO')) IS NULL)) and LASTUPDATEDBY is not null );
这里的正则会先筛掉格式明显不对的字符串,再对格式合法的字符串做日期有效性校验(比如2022-02-30这种格式正确但日期无效的情况)。
方法3:临时关闭严格SQL模式
如果以上两种方法都不适用,可以临时修改会话级的SQL模式,关闭严格检查,执行完后再恢复原配置:
-- 保存当前SQL模式 SET @OLD_SQL_MODE = @@SQL_MODE; -- 移除严格模式配置 SET SQL_MODE = REPLACE(@@SQL_MODE, 'STRICT_TRANS_TABLES', ''); SET SQL_MODE = REPLACE(@@SQL_MODE, 'STRICT_ALL_TABLES', ''); -- 创建临时表 DROP TEMPORARY TABLE IF EXISTS my_temp_id_table; CREATE TEMPORARY TABLE my_temp_id_table( SELECT DB_ID from verkehrsweg v where DB_ID in ( SELECT DB_ID FROM verkehrsweg v1 where STR_TO_DATE(v1.LASTUPDATEDBY, GET_FORMAT(DATE,'ISO')) is null) and LASTUPDATEDBY is not null ); -- 恢复原来的SQL模式 SET SQL_MODE = @OLD_SQL_MODE;
注意:这种方法会临时放宽会话的SQL检查,执行完后一定要恢复原模式,避免影响其他业务操作。
结果验证
结合你的表结构:
- DB_ID=3的
LASTUPDATEDBY是'hallo',会被筛选进临时表 - DB_ID=1是合法日期,不会被选中
- DB_ID=2、4的
LASTUPDATEDBY是NULL,会被and LASTUPDATEDBY is not null排除
以上方法都能正确完成需求,且不会触发1411错误。
内容的提问来源于stack exchange,提问作者Torben

