mysql别悄悄帮我做事了

背景:

书接上文,当我在接收工单消息时,直接以字符串(String)的形式接收了日期信息,而没有将其转换为日期(Date)格式,而数据表中是日期格式。这样一来,在设计实体类的时候,我也相应地使用了字符串类型来定义这个字段。当时用的mysql测试的没问题,线上环境用的pgsql,直接报错了。

原因:

实体类是String字段,数据表中是Date类型。mysql有隐式类型转换,没有报错,pgsql对类型检查更加严格,直接会报错。

MySQL 在处理数据时,会进行隐式类型转换。将一个字符串插入到日期字段时,MySQL 会尝试将字符串转换为日期。如果转换成功,则插入成功;如果失败,则可能插入 NULL 或者报错。

java 复制代码
String publishDate = sysDictInfoWithVersionFromDTO.getPublishDate();
// 解析publishDate为时间
if (publishDate == null || publishDate.isEmpty()) {
    throw new IllegalArgumentException("生效时间不能为空");
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // 根据实际日期格式调整
Date parsedDate;
try {
    parsedDate = dateFormat.parse(publishDate);
    // 进一步处理 parsedDate
} catch (ParseException e) {
    throw new IllegalArgumentException("Invalid date format: " + publishDate, e);
}

MySQL 对数据类型转换的底层机制

MySQL 在处理数据类型转换时,相对宽松。

1. 隐式类型转换:

MySQL 会在必要时自动进行隐式类型转换,以适应目标列的数据类型。这种转换通常发生在插入数据、更新数据或执行查询时。

2. 日期格式解析:

MySQL 会尝试解析字符串,以确定其是否符合日期格式。如果字符串格式正确,MySQL 会将其转换为 DATE 类型。如果格式不正确,MySQL 会尝试解析并可能插入一个默认值(如 0000-00-00)或抛出错误。

3. 默认值和错误处理:

如果转换失败,MySQL 会根据配置和上下文决定如何处理。例如,如果 sql_mode 设置为 STRICT_TRANS_TABLES 或 STRICT_ALL_TABLES,MySQL 会抛出错误。否则,它可能会插入一个默认值。

4. 示例:

当我传入一个String类型到一个日期类型,mysql会尝试转换,同时传入的字符串是"2024-11-12",它会试着解析这个字符串,看能不能转成 DATE 类型。是的话直接插入2024-11-12 00:00:00,所以当时流程可以走通。

底层机制详解

1. 隐式类型转换

MySQL 的隐式类型转换机制主要依赖于其内部的类型转换函数。这些函数在 SQL 执行过程中被调用,以确保数据类型的一致性。例如:

STR_TO_DATE(str, format):将字符串转换为日期。

DATE(str):将字符串转换为日期。

CAST(expr AS type):将表达式转换为目标类型。

字符串到数字的转换
sql 复制代码
  - MySQL 会尝试将字符串转换为数字。如果字符串以数字开头,MySQL 会提取前面的数字部分进行转换。
  - 如果字符串不能转换为数字,MySQL 会将其视为 0。
  SELECT 1 + '1'; -- 结果为 2
  SELECT 1 + '1a'; -- 结果为 2,因为 '1a' 被转换为 1
  SELECT 1 + 'a1'; -- 结果为 1,因为 'a1' 被转换为 0
数字到字符串的转换
arduino 复制代码
- 当需要将数字与字符串连接时,MySQL 会自动将数字转换为字符串。
SELECT CONCAT('The number is ', 1); -- 结果为 'The number is 1'
日期和时间的转换
sql 复制代码
  - `STR_TO_DATE` 函数将字符串转换为日期。
  - `DATE` 函数从日期时间字符串中提取日期部分。
  SELECT STR_TO_DATE('2024-11-13', '%Y-%m-%d'); -- 结果为 '2024-11-13'
  SELECT DATE('2024-11-13 12:34:56'); -- 结果为 '2024-11-13'
比较操作中的类型转换
ini 复制代码
  - 在比较操作中,MySQL 会尝试将字符串转换为数字进行比较。
  SELECT '1' = 1; -- 结果为 1 (true)
  SELECT '1a' = 1; -- 结果为 1 (true),因为 '1a' 被转换为 1
  SELECT 'a1' = 1; -- 结果为 0 (false),因为 'a1' 被转换为 0
NULL 值的处理
sql 复制代码
  - 任何与 `NULL` 的运算结果都是 `NULL`。
  - `IFNULL` 函数可以用来处理 `NULL` 值,提供一个默认值。
  SELECT 1 + NULL; -- 结果为 NULL
  SELECT IFNULL(1 + NULL, 0); -- 结果为 0
字符集和排序规则的转换
ini 复制代码
 - MySQL 会自动处理字符集和排序规则的转换,以确保字符串比较的正确性。
SELECT 'abc' = _utf8mb4'abc'; -- 结果为 1 (true)

需要类型转换时,使用显式的转换函数(如 CASTCONVERTSTR_TO_DATE 等),以提高代码的可读性和可维护性。

sql 复制代码
SELECT CAST('1' AS SIGNED) + 1; -- 结果为 2
SELECT CONCAT('The number is ', CAST(1 AS CHAR)); -- 结果为 'The number is 1'

2. 日期格式解析

MySQL 使用内置的日期解析器来处理日期字符串。解析器会尝试识别常见的日期格式,如 YYYY-MM-DD、YYYYMMDD、YY-MM-DD 等。如果字符串格式正确,解析器会返回一个有效的 DATE 值。

3. 默认值和错误处理

MySQL 的行为受 sql_mode 系统变量的影响。sql_mode 定义了 MySQL 的操作模式,包括数据验证和错误处理策略。

常见的 SQL_MODE
模式名称 描述
STRICT_TRANS_TABLES 对事务表(如 InnoDB)启用严格模式。严格模式:如果插入或更新操作违反了数据类型约束,MySQL 会抛出错误而不是警告,并且不会执行该操作。
STRICT_ALL_TABLES 对所有表(包括 MyISAM 和 InnoDB)启用严格模式。
NO_ZERO_DATE 禁止插入零日期('0000-00-00')。
NO_ZERO_IN_DATE 禁止插入部分零日期(如 '2023-00-00')。
ERROR_FOR_DIVISION_BY_ZERO 当除数为零时,返回错误而不是 NULL。
ONLY_FULL_GROUP_BY 要求 GROUP BY 子句中包含所有选择列,除非这些列是聚合函数的结果。
ANSI_QUOTES 允许使用双引号来表示标识符(如表名和列名),而不是字符串。
PIPES_AS_CONCAT 将 || 作为字符串连接操作符,而不是逻辑或操作符。
IGNORE_SPACE 允许在函数名和左括号之间有空格。
TRADITIONAL 包含多个严格模式和其他限制,使 MySQL 行为更接近于传统 SQL 标准。

查看当前的 SQL_MODE

scss 复制代码
SELECT @@sql_mode;

设置 SQL_MODE

ini 复制代码
配置文件设置
在 my.cnf 或 my.ini 文件中添加或修改以下内容:
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,ONLY_FULL_GROUP_BY'
动态设置
SET SESSION sql_mode='STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,ONLY_FULL_GROUP_BY';
全局设置(已经打开的会话不会立即受到新设置的影响)
SET GLOBAL sql_mode='STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,ONLY_FULL_GROUP_BY';

总结

mysql真好用,嘿嘿。其实写完代码,还是要测试一下pg环境的,当时只测试了mysql环境下的情况。还好系统还没正式投入使用。

相关推荐
捂月1 小时前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
瓜牛_gn1 小时前
依赖注入注解
java·后端·spring
Estar.Lee1 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪1 小时前
Django:从入门到精通
后端·python·django
一个小坑货1 小时前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet272 小时前
【Rust练习】22.HashMap
开发语言·后端·rust
uhakadotcom2 小时前
如何实现一个基于CLI终端的AI 聊天机器人?
后端
Iced_Sheep3 小时前
干掉 if else 之策略模式
后端·设计模式
XINGTECODE3 小时前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
程序猿进阶3 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露