【DB】Oracle转MySQL

大小写规则

Oracle

在 Oracle SQL 中,表名和字段名的大小写规则与 MySQL 不同,核心取决于命名时是否使用双引号。

不使用双引号的情况(默认行为)

表名和字段名会被自动转换为大写,且查询时不区分大小写。

    • 创建表时写 create table user_info (...),Oracle 会将表名存储为 USER_INFO
    • 查询时写 select user_name from User_InfoSELECT USER_NAME FROM user_info,均能正常匹配,因为 Oracle 会先将表名 / 字段名转换为大写再查找。

此时,表名和字段名本质上是大小写不敏感的,无论定义或查询时用大写、小写还是混合大小写,最终都会被统一处理为大写。

使用双引号的情况(强制大小写)

若创建表或字段时用双引号包裹名称,则 Oracle 会严格保留大小写,且查询时必须使用完全匹配的大小写(也需用双引号包裹)。

    • 创建表:create table "UserInfo" ("userName" varchar2(50))
      • 此时表名存储为 UserInfo(首字母大写),字段名存储为 userName(驼峰式)。
    • 查询时必须写:select "userName" from "UserInfo"
      • 若写成 select username from UserInfoSELECT "UserName" FROM "userinfo" 都会报错(大小写不匹配)。

MySQL

在 MySQL 中,表名和字段名的大小写敏感性取决于两个关键因素:操作系统的文件系统和 lower_case_table_names 系统变量。

表名的大小写规则

核心配置:lower_case_table_names(控制表名的大小写处理)

查询SQL:SHOW VARIABLES LIKE 'lower_case_table_names';

  • 默认值随操作系统不同:
    • Linux/Unix 系统:默认值为 0(大小写敏感)
      • 表名会严格区分大小写(如 user 和 User 是两个不同的表),且存储时保留创建时的大小写。
    • Windows 系统:默认值为 1(大小写不敏感)
      • 表名不区分大小写(如 user 和 User 视为同一个表),存储时会转换为小写。
    • MacOS 系统:若使用 APFS 文件系统,默认值为 2(存储时保留大小写,但查询时不区分);若使用旧文件系统,可能同 Linux 规则。

若将 lower_case_table_names 设为 1(强制不敏感),MySQL 会将所有表名转换为小写存储,查询时无论大小写均匹配。修改此变量需重启 MySQL 生效,且建议初始化数据库时就确定配置,避免后续表名冲突。

字段名的大小写规则

字段名的大小写不敏感(与配置无关)

无论操作系统或 lower_case_table_names 如何设置,MySQL 的字段名在查询和存储时均不区分大小写。

例如:SELECT username FROM userSELECT UserName FROM USER 效果相同。

例外:关键字作为字段名

  • 若字段名使用 MySQL 关键字(如order、select),需用反引号 ````` 包裹,此时仍不区分大小写,但建议统一命名规范避免混淆。

实践

跨平台开发:建议统一使用小写表名和字段名,并避免大小写混用,减少因环境差异导致的问题。

Linux 环境:若必须区分表名大小写,需确保代码中表名与创建时完全一致;否则建议将 lower_case_table_names 设为 1(需谨慎操作)。

字段命名:无需考虑大小写,但建议遵循统一风格(如蛇形命名 user_name)。

时间转换

核心转换函数

转换场景 Oracle 函数 MySQL 函数
字符串 -> 日期时间 TO_DATE(字符串, 格式) STR_TO_DATE(字符串, 格式)
日期时间 → 字符串 TO_CHAR(日期, 格式) DATE_FORMAT(日期, 格式)
Unix 时间戳 → 日期 无内置函数(需手动计算) FROM_UNIXTIME(时间戳)
日期 → Unix 时间戳 无内置函数(需手动计算) UNIX_TIMESTAMP(日期)

格式掩码

功能 Oracle 格式符 MySQL 格式符 说明
四位年份 YYYY %Y MySQL 需加 % 前缀
两位年份 YY %y 同上
月份(数字) MM %m 同上
月份(英文缩写) MON %b Oracle 不区分大小写, MySQL 依赖系统语言
月份(英文全称) MONTH %M 同上
日期(数字) DD %d MySQL 需加 % 前缀
小时(24 小时制) HH24 %H Oracle 用 HH24,MySQL 用 %H
小时(12 小时制) HH %h 同上
分钟 MI %i Oracle 用 MI,MySQL 用 %i
SS %s MySQL 需加 % 前缀
上午 / 下午 AM / PM %p Oracle 可省略格式符(自动识别), MySQL 必须显式用 %p

转换逻辑与行为

转换失败的处理

Oracle 的 TO_DATE 若格式不匹配,会直接抛出错误(如 ORA-01830)。

MySQL 的 STR_TO_DATE 转换失败会返回 NULL,不报错(需手动校验结果)。

sql 复制代码
-- Oracle:报错(格式不匹配) 
SELECT TO_DATE('2023/10/01', 'YYYY-MM-DD') FROM DUAL; 

-- MySQL:返回 NULL 
SELECT STR_TO_DATE('2023/10/01', '%Y-%m-%d');

默认格式的依赖

Oracle 有默认日期格式(由 NLS_DATE_FORMAT 控制,通常为 DD-MON-RR),若省略格式符,TO_DATE 会按默认格式解析。

  • 示例:TO_DATE('01-OCT-23') 可正常解析(依赖默认格式)。

MySQL 无默认格式,STR_TO_DATE 必须显式指定格式掩码(除非字符串是标准格式,如 '2023-10-01' 可被自动识别)。

时间戳处理

Oracle 没有直接处理 Unix 时间戳的函数,需通过计算转换。

  • 示例: TO_DATE('1970-01-01', 'YYYY-MM-DD') + 时间戳/(24*60*60)

MySQL 原生支持 FROM_UNIXTIMEUNIX_TIMESTAMP

  • 示例:SELECT FROM_UNIXTIME(1696103400); -- 2023-10-01 14:30:00

常用转换场景对比示例

sql 复制代码
# 字符串转日期(带时间)
-- oracle
TO_DATE('2023-10-01 14:30:00', 'YYYY-MM-DD HH24:MI:SS');

-- MySQL
STR_TO_DATE('2023-10-01 14:30:00', '%Y-%m-%d %H:%i:%s');


# 日期转字符串(中文格式)
-- oracle
TO_CHAR(SYSDATE, 'YYYY"年"MM"月"DD"日"');

-- MySQL
DATE_FORMAT(NOW(), '%Y年%m月%d日');


# 提取年份
-- oracle
EXTRACT(YEAR FROM SYSDATE);

-- MySQL
YEAR(NOW());

DATE_FORMAT(NOW(), '%Y');


# 12小时制时间转换
-- oracle
TO_CHAR(SYSDATE, 'HH:MI:SS AM');

-- MySQL
DATE_FORMAT(NOW(), '%h:%i:%s %p');

字符串拼接

Oracle

Oracle 主要通过两种方式实现字符串拼接:

  • || 运算符:Oracle 中最常用的字符串拼接方式,直接使用双竖线连接多个字符串。

    sql 复制代码
    SELECT 'Hello' || ' ' || 'World' AS result FROM dual; 
    -- 结果:Hello World
  • CONCAT 函数:Oracle 的 CONCAT 函数仅支持两个参数 ,如果需要拼接多个字符串,必须嵌套使用。

    sql 复制代码
    SELECT CONCAT(CONCAT('Hello', ' '), 'World') AS result FROM dual; 
    -- 结果:Hello World

MySQL

MySQL 支持三种字符串拼接方式:

  • CONCAT 函数:MySQL 的 CONCAT 函数支持多个参数,直接按顺序拼接所有参数。

    sql 复制代码
    SELECT CONCAT('Hello', ' ', 'World') AS result; 
    -- 结果:Hello World
  • || 运算符 :在 MySQL 中,|| 的行为取决于系统变量 sql_mode

    • sql_mode 包含 PIPES_AS_CONCAT,则 || 作为字符串拼接运算符,功能同 CONCAT()
    sql 复制代码
    SET sql_mode = 'PIPES_AS_CONCAT'; 
    SELECT 'Hello' || ' ' || 'World' AS result; 
    -- 结果:Hello World
    • 若不包含该模式,|| 会被当作逻辑 OR 运算符(返回 0 或 1),可能导致非预期结果。
    sql 复制代码
    SET sql_mode = ''; 
    -- 默认模式(不含 PIPES_AS_CONCAT) 
    SELECT 'Hello' || 'World' AS result; 
    -- 结果:1(逻辑真)
  • CONCAT_WS() 函数 :用于按指定分隔符拼接字符串(WSWith Separator),第一个参数为分隔符,后续参数为待拼接字符串。

    sql 复制代码
    SELECT CONCAT_WS(' ', 'Hello', 'World', '!') AS result; 
    -- 结果:Hello World !

    空值(NULL)处理

核心通用规则

  1. NULL ≠ 任何值:包括 NULL 本身。判断 NULL 必须用 IS NULLIS NOT NULL,不能用 = 或 !=。
    示例:NULL = 1 结果为 NULL(非真非假),NULL IS NULL 结果为 TRUE。
  2. NULL 参与运算的结果为 NULL
    示例:1 + NULL'abc' || NULL (Oracle)CONCAT('abc', NULL) (MySQL) 的结果均为 NULL 。
  3. 聚合函数忽略 NULL:SUM()AVG()MAX()MIN()COUNT(column) 会自动排除 NULL 值;仅 COUNT(*) 会统计包含 NULL 的行。

空值处理

字符串拼接中空值的处理

Oracle

若拼接的字符串中包含 NULL,则整个结果为 NULL(|| 运算符和 CONCAT() 函数均遵循此规则)。

sql 复制代码
SELECT 'Hello' || NULL || 'World' AS result FROM dual; 

SELECT CONCAT('Hello', NULL) AS result FROM dual; 

-- 结果都为NULL
MySQL

CONCAT() 函数会忽略 NULL,仅拼接非空字符串;若使用 ||(需开启 PIPES_AS_CONCAT 模式),则与 Oracle 一致(含 NULL 则结果为 NULL)。

sql 复制代码
SELECT CONCAT('Hello', NULL, 'World') AS result; 
-- 结果:HelloWorld(忽略 NULL) 

SET sql_mode = 'PIPES_AS_CONCAT'; 
SELECT 'Hello' || NULL || 'World' AS result; 
-- 结果:NULL(与 Oracle 一致)

排序(ORDER BY)中空值的位置

Oracle

默认将 NULL 视为 "最大值",排序时 NULL 会排在所有非空值之后(升序 ASC);若需调整,需显式指定 NULLS FIRSTNULLS LAST

sql 复制代码
-- 表 t 含数据:1, 3, NULL, 2 
SELECT col FROM t ORDER BY col ASC; 
-- 结果:1,2,3,NULL(NULL 在最后) 

SELECT col FROM t ORDER BY col ASC NULLS FIRST; 
-- 结果:NULL,1,2,3(NULL 在最前)
MySQL

默认将 NULL 视为 "最小值",排序时 NULL 会排在所有非空值之前(升序 ASC);且 MySQL 不支持 NULLS FIRST/LAST 语法,需通过函数间接调整(如 ORDER BY IF(col IS NULL, 0, 1), col)。

sql 复制代码
-- 表 t 含数据:1, 3, NULL, 2 
SELECT col FROM t ORDER BY col ASC; 
-- 结果:NULL,1,2,3(NULL 在最前) 

SELECT col FROM t ORDER BY IF(col IS NULL, 1, 0), col ASC; 
-- 结果:1,2,3,NULL(间接调整 NULL 到最后)

空字符串('')与 NULL 的关系

Oracle

不区分空字符串和 NULL,将空字符串('')视为 NULL。因此 '' IS NULL 结果为 TRUE,且无法存储真正的空字符串(插入 '' 会自动转为 NULL)。

sql 复制代码
SELECT NVL('', '默认值') AS result FROM dual; 
-- 结果:默认值('' 被视为 NULL) 

INSERT INTO t(col) VALUES (''); 
-- 实际插入的是 NULL
MySQL

严格区分空字符串和 NULL:空字符串('')是有效值(长度为 0 的字符串),'' IS NULL 结果为 FALSE,可正常存储和使用。

sql 复制代码
SELECT IFNULL('', '默认值') AS result; 
-- 结果:''('' 不视为 NULL) 

INSERT INTO t (col) VALUES (''); 
-- 实际插入的是空字符串(非 NULL)

兼容与替代方案

如果需要在 Oracle 与 MySQL 两种数据库中兼容处理 NULL 值,COALESCE 函数是更好的选择(两者均支持)。

sql 复制代码
SELECT COALESCE(NULL, '默认值') AS result; 
-- 结果:默认值

COALESCE 支持多个参数(如 COALESCE(a, b, c)),返回第一个非 NULL 的参数,功能更灵活。

字符串聚合函数

字符串聚合函数:用于将分组内的某列值拼接成一个字符串。

Oracle

LISTAGG 是 Oracle 首先推出的字符串聚合函数。

sql 复制代码
LISTAGG(列名, '分隔符') [WITHIN GROUP (ORDER BY 排序字段)] [OVER (PARTITION BY 分组字段)]

关键参数

  • 第一个参数是要聚合的列,第二个参数是分隔符(无默认,必须指定)。
  • WITHIN GROUP (ORDER BY):可选,指定拼接前的排序规则。
  • OVER (PARTITION BY):可选,用于窗口函数场景(替代 GROUP BY 分组)。

使用 GROUP BY 分组

sql 复制代码
SELECT class, LISTAGG(name, ',') WITHIN GROUP (ORDER BY name) AS students 
FROM student GROUP BY class;

-- 结果
class | students
------|----------
1班   | Alice,Bob
2班   | Charlie

用窗口函数(不分组,保留所有行)

sql 复制代码
SELECT id, name, class, LISTAGG(name, ',') WITHIN GROUP (ORDER BY name) OVER (PARTITION BY class) AS class_students 
FROM student;

-- 结果
id | name    | class | class_students
---|---------|-------|----------------
1  | Alice   | 1班   | Alice,Bob
2  | Bob     | 1班   | Alice,Bob
3  | Charlie | 2班   | Charlie

MySQL

GROUP_CONCAT 是 MySQL 及其衍生数据库(如 MariaDB)提供的字符串聚合函数,用于将分组内的某列值拼接成一个字符串。

sql 复制代码
GROUP_CONCAT([DISTINCT] 列名 [ORDER BY 排序字段 [ASC|DESC]] [SEPARATOR '分隔符'])

关键参数

  • DISTINCT:可选,去重后再拼接。
  • ORDER BY:可选,指定拼接前的排序规则。
  • SEPARATOR:可选,指定分隔符(默认是逗号 ,)。
id name class
1 Alice 1 班
2 Bob 1 班
3 Charlie 2 班

按班级分组,拼接学生姓名

sql 复制代码
SELECT class, GROUP_CONCAT(name) AS students 
FROM student 
GROUP BY class;

-- 结果
class | students
------|----------
1班   | Alice,Bob
2班   | Charlie

带排序和自定义分隔符

sql 复制代码
SELECT class, GROUP_CONCAT(name ORDER BY name DESC SEPARATOR ';') AS students 
FROM student 
GROUP BY class;

class | students 
------|---------- 
1班 | Bob;Alice 
2班 | Charlie
相关推荐
yuniko-n4 小时前
【力扣 SQL 50】连接
数据库·后端·sql·算法·leetcode
自己收藏学习4 小时前
统计订单总数并列出排名
数据库·sql·mysql
TiAmo zhang4 小时前
SQL Server 2019实验 │ 安装及其管理工具的使用
数据库·sqlserver
MZZDX5 小时前
MySQL相关知识总结
数据库·mysql
青山撞入怀11147 小时前
sql题目练习——聚合函数
数据库·sql
disanleya7 小时前
MySQL默认端口为何是3306?修改后如何管理?
数据库·mysql·adb
川石课堂软件测试10 小时前
MySQL数据库之DBA命令
数据库·网络协议·mysql·http·单元测试·prometheus·dba
ybb_ymm12 小时前
mysql8在linux下的默认规则修改
linux·运维·数据库·mysql