MySQL的null,substring,dual

注:本篇文档是一个大杂烩。

环境

  • MySQL 8.0.28

null

我们知道,在SQL语句里, null 是一个特殊的值,它类似于Java里的 null ,表示一个值为空或者未知。

比如一个 person 表,有 nameage 等列,其中 ageint 类型。如果一个人的年龄未知,那么其 age 如何设置呢?在这里,使用 null 值,就表示这个人的年龄未知。这比设置成一个特定值(比如 0-1)更合适。

注意: null 和数值 0 ,空字符串 '' 是不同的。

关于null,最容易出错的一点是:在判断一个表达式是不是null时,需要注意不是用 xxx = null ,而是 xxx is null

现有表 t1 如下:
在这里插入图片描述

select c1 is null from t1;
+------------+
| c1 is null |
+------------+
|          0 |
|          1 |
+------------+
2 rows in set (0.01 sec)

可见, xxx is null 是一个布尔表达式,会返回true/false,也就是1/0。

也可以把 xxx is null 用到where条件里:

select * from t1
where c1 is null;

在这里插入图片描述

现在问题来了,如果在判断null时,使用了 xxx = null ,结果会是什么呢?

select * from t1
where c1 = null;

在这里插入图片描述

可见,结果里一条记录也没有,这是为什么呢?

这是因为,null和其它任何值(包括null值)比较,结果都是null,所以 where c1 = null 永远返回null,也就是说where条件不满足,所以结果里一条记录也没有。

这一点一定要小心,否则很容易出错。

coalesce

coalesce() 函数有两个参数,如果第一个参数不是null,就返回第一个参数,否则返回第二个参数。

select coalesce(1, 2);
+----------------+
| coalesce(1, 2) |
+----------------+
|              1 |
+----------------+
1 row in set (0.01 sec)

上例中,第一个参数不是null,所以返回第一个参数值。

select coalesce(null, 2);
+-------------------+
| coalesce(null, 2) |
+-------------------+
|                 2 |
+-------------------+
1 row in set (0.00 sec)

上例中,第一个参数是null,所以返回第二个参数值。

现在,我们用 coalesce() 来验证一下 xxx = null 的返回值:

select coalesce(c1 = null, 2) from t1;
+------------------------+
| coalesce(c1 = null, 2) |
+------------------------+
|                      2 |
|                      2 |
+------------------------+
2 rows in set (0.00 sec)

可见, xxx = null 的返回值确实是null。

substring

substring() 函数有3个参数:

  • 字符串
  • 起始下标(从1开始)
  • 子串长度

比如:

select substring('abcde', 3, 2);
+--------------------------+
| substring('abcde', 3, 2) |
+--------------------------+
| cd                       |
+--------------------------+
1 row in set (0.01 sec)

问题又来了:如果下标越界,结果会是什么?

看下面的SQL:

select substring('abcde', 9, 2);
+--------------------------+
| substring('abcde', 9, 2) |
+--------------------------+
|                          |
+--------------------------+
1 row in set (0.01 sec)

从结果里,似乎看不出来是null还是 '' ,我们用 coalesce() 来判断一下:

select coalesce(substring('abcde', 9, 2), 'zzz');
+-------------------------------------------+
| coalesce(substring('abcde', 9, 2), 'zzz') |
+-------------------------------------------+
|                                           |
+-------------------------------------------+
1 row in set (0.00 sec)

可见,如果 substring() 下标越界了,会返回 '' ,而不是null值。

这一点挺奇怪的,因为我问了DeepSeek和豆包,它们都说下标越界时,返回值是null。

前面提到,判断 '' 和null是不一样的,前者用 = ,后者用 is

比如:

select substring('abcde', 9, 1) = substring('hijkl', 9, 1);
+-----------------------------------------------------+
| substring('abcde', 9, 1) = substring('hijkl', 9, 1) |
+-----------------------------------------------------+
|                                                   1 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

如果知道substring不会返回null,那我们就可以全部直接用 = 来判断了(在非null值上使用 is 会报错)。

dual

在上面的例子里,你可能已经发现了:如果是计算一个表达式的值(和表无关),可以直接用 select 而无需 from ,比如:

select 1 + 1;
+-------+
| 1 + 1 |
+-------+
|     2 |
+-------+
1 row in set (0.00 sec)

如果不习惯这种写法,可以使用MySQL提供的一个虚拟表 dual

select 1 + 1 from dual;
+-------+
| 1 + 1 |
+-------+
|     2 |
+-------+
1 row in set (0.00 sec)

结果也是一样的。

注:在Db2里的写法是:

select 1 + 1 from sysibm.sysdummy1

考一考

下面SQL的运行结果是什么?

select 1 = null, 1 > null, 1 < null, 1 <> null, 1 is null

答案是:

null  null  null  null  0

下面SQL的运行结果是什么?

select null = null, null > null, null < null, null <> null, null is null

答案是:

null  null  null  null  1

总结:null和其它值(包括null值)比较(大于、小于、等于、不等于),结果都是null,只有 xxx is null 才会返回true/false。