×
技术社区 >  技术博客 >  OceanBase 中 MySQL/Oracle 模式中的 NULL 与空字符串

OceanBase 中 MySQL/Oracle 模式中的 NULL 与空字符串

OceanBase 一套内核兼容 MySQL 和 Oracle 两种租户模式,但对NULL、''(空串)的底层处理逻辑天差地别。本文基于 OB V4.2.1.7 实测验证,把两种模式下存储、查询、COUNT 统计、唯一索引四大核心场景讲透,看完直接规避线上故障。

一、总结

【NULL 与空字符串】一句话核心总结:

  • MySQL 模式:空串 '' 是普通字符串,与 NULL 完全不同;

  • Oracle 模式:空串 '' 被视为 NULL,两者等价;

操作场景 OB Oracle 模式 OB MySQL 模式
插入空串 '' 自动转为 NULL 保持为空串 ''
空串是否为 NULL 是 否
NULL 与空串比较 不相等 不相等
COUNT(column) 不统计 NULL,不统计空串 不统计 NULL,统计空串
唯一索引中多个 NULL 不冲突(允许) 不冲突(允许)
唯一索引中多个空串 不冲突(因为被转为 NULL) 冲突(不允许)

二、验证步骤-MySQL 模式

2.1 准备环境

创建 test_mysql_260527 表并插入 3 条数据,分别是:空字符串,NULL,正常值;

mysql -h192.168.xxx.xxx -P2883 -uroot@mysql_251126#ob_4255_251126 -p'xxx' -A -Dtest

CREATE TABLE test_mysql_260527 ( id INT, col_1 VARCHAR(50));           -- 创建测试表
INSERT INTO test_mysql_260527 VALUES (1, ''), (2, NULL), (3, 'abc');   -- 插入测试数据,分别是:空字符串,NULL,正常值;
-- DROP TABLE test_mysql_260527;  -- 清理测试表
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 6572857
Server version: 5.6.25 OceanBase 4.2.1.7 (r107010022024060601-fe709b4735794f5e577d5b14f91d9183a12f101e) (Built Jun  6 2024 01:33:55)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [test]> CREATE TABLE test_mysql_260527 ( id INT, col_1 VARCHAR(50));           -- 创建测试表
Query OK, 0 rows affected (0.39 sec)

MySQL [test]> INSERT INTO test_mysql_260527 VALUES (1, ''), (2, NULL), (3, 'abc');   -- 插入测试数据,分别是:空字符串,NULL,正常值;
Query OK, 3 rows affected (0.08 sec)
Records: 3  Duplicates: 0  Warnings: 0
2.2 验证 NULL 值和空字符串是否等价

在 V 4.2.1.7 版本的 MySQL 租户模式中,验证【null 值】和【''(即空值,空字符串)】的区别;验证结果:在 OB MySQL 模式中,以上 2 者是不等价的;

-- 【sql-01】查询空字符串(应返回 id=1 的行)
SELECT * FROM test_mysql_260527 WHERE col_1 = '';

-- 【sql-02】查询 NULL(应返回 id=2 的行)
SELECT * FROM test_mysql_260527 WHERE col_1 IS NULL;

-- 【sql-03】= NULL 语法不会返回任何结果(错误用法)
SELECT * FROM test_mysql_260527 WHERE col_1 = NULL;  -- 返回空集
MySQL [test]> SELECT * FROM test_mysql_260527 WHERE col_1 = '';
+------+-------+
| id   | col_1 |
+------+-------+
|    1 |       |
+------+-------+
1 row in set (0.01 sec)

MySQL [test]> SELECT * FROM test_mysql_260527 WHERE col_1 IS NULL;
+------+-------+
| id   | col_1 |
+------+-------+
|    2 | NULL  |
+------+-------+
1 row in set (0.01 sec)

MySQL [test]> SELECT * FROM test_mysql_260527 WHERE col_1 = NULL;  -- 返回空集
Empty set (0.00 sec)
2.3 验证 count 函数对 null 和 空字符串的处理

在 OB MySQL 模式中:count(col_1) 函数会忽略 NULL 值,所以预期结果为 2;

-- 【sql-04】验证 count 函数对 null 和 空字符串的处理(预期结果为:2)
SELECT count(col_1) FROM test_mysql_260527;
MySQL [test]> SELECT count(col_1) FROM test_mysql_260527;
+--------------+
| count(col_1) |
+--------------+
|            2 |
+--------------+
1 row in set (0.01 sec)
2.4 验证对唯一索引的影响

在 OB MySQL 模式中:唯一索引是允许 NULL 值重复的;空字符串是不允许重复的;所以第 1 条 insert 语句成功,第 2 条 insert 语句失败;

-- 【sql-05】验证对唯一索引的影响:
CREATE UNIQUE INDEX idx_unique_col_1 ON test_mysql_260527 (col_1); 	-- 在 col_1 字段上创建唯一索引
INSERT INTO test_mysql_260527 VALUES (4, NULL);
INSERT INTO test_mysql_260527 VALUES (5, '');
MySQL [test]> CREATE UNIQUE INDEX idx_unique_col_1 ON test_mysql_260527 (col_1);
Query OK, 0 rows affected (0.81 sec)

MySQL [test]> INSERT INTO test_mysql_260527 VALUES (4, NULL);
Query OK, 1 row affected (0.01 sec)

MySQL [test]> INSERT INTO test_mysql_260527 VALUES (5, '');
ERROR 1062 (23000): Duplicate entry '' for key 'idx_unique_col_1'

三、验证步骤-Oracle 模式

3.1 准备环境

创建 test_oracle_260527 表并插入 3 条数据,分别是:空字符串,NULL,正常值;

obclient -h192.168.xxx.xxx -P2883 -uSYS@oracle_250917#ob_4255_251126 -p'xxx' -A

CREATE TABLE test_oracle_260527 ( id INT, col_1 VARCHAR(50));           -- 创建测试表
INSERT INTO test_oracle_260527 VALUES (1, ''), (2, NULL), (3, 'abc');   -- 插入测试数据,分别是:空字符串,NULL,正常值;
-- DROP TABLE test_oracle_260527;  -- 清理测试表
Welcome to the OceanBase.  Commands end with ; or \g.
Your OceanBase connection id is 6572969
Server version: OceanBase 4.2.1.7 (r107010022024060601-fe709b4735794f5e577d5b14f91d9183a12f101e) (Built Jun  6 2024 01:33:55)

Copyright (c) 2000, 2018, OceanBase and/or its affiliates. All rights reserved.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

obclient(SYS@oracle_250917)[SYS]> CREATE TABLE test_oracle_260527 ( id INT, col_1 VARCHAR(50));           -- 创建测试表
Query OK, 0 rows affected (0.369 sec)

obclient(SYS@oracle_250917)[SYS]> INSERT INTO test_oracle_260527 VALUES (1, ''), (2, NULL), (3, 'abc');   -- 插入测试数据,分别是:空字符串,NULL,正常值;
Query OK, 3 rows affected (0.054 sec)
Records: 3  Duplicates: 0  Warnings: 0
3.2 验证 NULL 值和空字符串是否等价

使用 insert into 语句插入【''(空字符串)】时,OB 会自动将其转换成 NULL 值并存储,但是在查询时(where 条件中),若使用【''(空字符串)】则不能查出数据,必须使用 IS NULL 或 IS NOT NULL;

-- 【sql-01】查询空字符串(返回空集)
SELECT * FROM test_oracle_260527 WHERE col_1 = '';

-- 【sql-02】查询 NULL(应返回 id = 1/2 的行)
SELECT * FROM test_oracle_260527 WHERE col_1 IS NULL;

-- 【sql-03】= NULL 语法不会返回任何结果(错误用法)(返回空集)
SELECT * FROM test_oracle_260527 WHERE col_1 = NULL; 
obclient(SYS@oracle_250917)[SYS]> SELECT * FROM test_oracle_260527 WHERE col_1 = '';
Empty set (0.002 sec)

obclient(SYS@oracle_250917)[SYS]> SELECT * FROM test_oracle_260527 WHERE col_1 IS NULL;
+------+-------+
| ID   | COL_1 |
+------+-------+
|    1 | NULL  |
|    2 | NULL  |
+------+-------+
2 rows in set (0.008 sec)

obclient(SYS@oracle_250917)[SYS]> SELECT * FROM test_oracle_260527 WHERE col_1 = NULL; 
Empty set (0.004 sec)
3.3 验证 count 函数对 null 和 空字符串的处理

在 OB Oracle 模式中:count(col_1) 函数会忽略 NULL 值,正常来说查询结果应该为 2,但是在 OB Oracle 模式中,会将【''(空字符串)】转换成 NULL 值并存储,所以查询结果预期为 1;

-- 【sql-04】验证 count 函数对 null 和 空字符串的处理(预期结果为:1)
SELECT count(col_1) FROM test_oracle_260527;
obclient(SYS@oracle_250917)[SYS]> SELECT count(col_1) FROM test_oracle_260527;
+--------------+
| COUNT(COL_1) |
+--------------+
|            1 |
+--------------+
1 row in set (0.003 sec)
3.4 验证对唯一索引的影响

在 OB Oracle 模式中:唯一索引是允许 NULL 值重复的;空字符串会被转换成 NULL 值并存储,所以也是允许重复的;即 2 条 insert 语句都能执行成功;

-- 【sql-05】验证对唯一索引的影响:
CREATE UNIQUE INDEX idx_unique_col_1 ON test_oracle_260527 (col_1); 	-- 在 col_1 字段上创建唯一索引
INSERT INTO test_oracle_260527 VALUES (4, NULL);
INSERT INTO test_oracle_260527 VALUES (5, '');
obclient(SYS@oracle_250917)[SYS]> CREATE UNIQUE INDEX idx_unique_col_1 ON test_oracle_260527 (col_1); 
Query OK, 0 rows affected (0.864 sec)

obclient(SYS@oracle_250917)[SYS]> INSERT INTO test_oracle_260527 VALUES (4, NULL);
Query OK, 1 row affected (0.003 sec)

obclient(SYS@oracle_250917)[SYS]> INSERT INTO test_oracle_260527 VALUES (5, '');
Query OK, 1 row affected (0.003 sec)

PS:本文关于以上所有验证都是在 V 4.2.1.7 版本的 OB 集群中进行。

精选推荐