大小写规则
Oracle
在 Oracle SQL 中,表名和字段名的大小写规则与 MySQL 不同,核心取决于命名时是否使用双引号。
不使用双引号的情况(默认行为)
表名和字段名会被自动转换为大写,且查询时不区分大小写。
- 例
- 创建表时写
create table user_info (...)
,Oracle 会将表名存储为USER_INFO
。 - 查询时写
select user_name from User_Info
或SELECT USER_NAME FROM user_info
,均能正常匹配,因为 Oracle 会先将表名 / 字段名转换为大写再查找。
- 创建表时写
此时,表名和字段名本质上是大小写不敏感的,无论定义或查询时用大写、小写还是混合大小写,最终都会被统一处理为大写。
使用双引号的情况(强制大小写)
若创建表或字段时用双引号包裹名称,则 Oracle 会严格保留大小写,且查询时必须使用完全匹配的大小写(也需用双引号包裹)。
- 例
- 创建表:
create table "UserInfo" ("userName" varchar2(50))
- 此时表名存储为
UserInfo
(首字母大写),字段名存储为userName
(驼峰式)。
- 此时表名存储为
- 查询时必须写:
select "userName" from "UserInfo"
- 若写成
select username from UserInfo
或SELECT "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 规则。
- Linux/Unix 系统:默认值为 0(大小写敏感)
若将 lower_case_table_names
设为 1(强制不敏感),MySQL 会将所有表名转换为小写存储,查询时无论大小写均匹配。修改此变量需重启 MySQL 生效,且建议初始化数据库时就确定配置,避免后续表名冲突。
字段名的大小写规则
字段名的大小写不敏感(与配置无关)
无论操作系统或 lower_case_table_names
如何设置,MySQL 的字段名在查询和存储时均不区分大小写。
例如:SELECT username FROM user
和 SELECT 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_UNIXTIME
和 UNIX_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 中最常用的字符串拼接方式,直接使用双竖线连接多个字符串。sqlSELECT 'Hello' || ' ' || 'World' AS result FROM dual; -- 结果:Hello World
-
CONCAT
函数:Oracle 的CONCAT
函数仅支持两个参数 ,如果需要拼接多个字符串,必须嵌套使用。sqlSELECT CONCAT(CONCAT('Hello', ' '), 'World') AS result FROM dual; -- 结果:Hello World
MySQL
MySQL 支持三种字符串拼接方式:
-
CONCAT
函数:MySQL 的CONCAT
函数支持多个参数,直接按顺序拼接所有参数。sqlSELECT CONCAT('Hello', ' ', 'World') AS result; -- 结果:Hello World
-
||
运算符 :在 MySQL 中,||
的行为取决于系统变量sql_mode
。- 若
sql_mode
包含PIPES_AS_CONCAT
,则||
作为字符串拼接运算符,功能同CONCAT()
。
sqlSET sql_mode = 'PIPES_AS_CONCAT'; SELECT 'Hello' || ' ' || 'World' AS result; -- 结果:Hello World
- 若不包含该模式,
||
会被当作逻辑 OR 运算符(返回 0 或 1),可能导致非预期结果。
sqlSET sql_mode = ''; -- 默认模式(不含 PIPES_AS_CONCAT) SELECT 'Hello' || 'World' AS result; -- 结果:1(逻辑真)
- 若
-
CONCAT_WS()
函数 :用于按指定分隔符拼接字符串(WS
即With Separator
),第一个参数为分隔符,后续参数为待拼接字符串。sqlSELECT CONCAT_WS(' ', 'Hello', 'World', '!') AS result; -- 结果:Hello World !
空值(NULL)处理
核心通用规则
- NULL ≠ 任何值:包括 NULL 本身。判断 NULL 必须用
IS NULL
或IS NOT NULL
,不能用 = 或 !=。
示例:NULL = 1
结果为 NULL(非真非假),NULL IS NULL
结果为 TRUE。 - NULL 参与运算的结果为 NULL
示例:1 + NULL
、'abc' || NULL (Oracle)
、CONCAT('abc', NULL) (MySQL)
的结果均为 NULL 。 - 聚合函数忽略 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 FIRST
或 NULLS 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