NOT NULL 拦的是「什么都没有」,不拦空字符串。 Demo 里 '' 进去了,LENGTH 是 0。
要拦它得加 CHECK (name <> '')。
非空是列的性质,插入和修改都管。 这一点和默认值正相反——默认值只管插入。
= NULL 永远给 0 行,因为「不知道」等不等于「不知道」的答案是「不知道」。
判断空只能用 IS NULL/IS NOT NULL。
上一篇讲默认值「只在没给的时候生效」,这一篇讲「给了空就拦」。
两个属性经常一起用:NOT NULL DEFAULT 0,
非空保证有值、默认值保证不给也有值。 这一篇的坑在「什么叫空」这个判断上。
一、不给值又没默认值,报错
name TEXT NOT NULL,插入时不给 name,直接报错。
报错文字是 NOT NULL constraint failed: demo_nn.name,连表名列名都告诉你了。
MySQL 那边是 Column 'name' cannot be null,意思一样、措辞不同。
报错的措辞不一样,但「哪一列空了」都能看出来。
二、空串能过,NULL 不能过
insert into 表 (id, name) values (2, '') 进去了,LENGTH 是 0。
非空约束只拦「什么都没有」,不拦「空字符串」。
这是一个真实存在的坑:前端提交了空输入框,后端拼成 '' 传进来,非空约束一句不拦,
库里就多了一行「名字是空字符串」的记录。
要拦它得加 CHECK (name <> '')。 注意这个 CHECK 对 NULL 是不拦的
(NULL 参与比较得「不知道」,不算假),所以两个要一起写:
name TEXT NOT NULL CHECK (name <> '')。
三、update 成 NULL 一样拦
非空是列的性质,不管你是插入还是修改,它都管。
Demo 里 UPDATE demo_nn SET name = NULL 直接报错。
这一点和默认值正相反:
默认值只管插入,非空插入修改都管。 一个管「进来的时候」,一个管「任何时候」。
四、非空与默认值是最佳搭档
status INTEGER NOT NULL DEFAULT 0——
非空保证「这列一定有值」,默认值保证「不给也有值」。
两个一起用,业务代码就可以完全不提这一列。
只写非空不写默认值,那每次插入都必须显式给值,漏一次报一次错,代码里到处是 0、
'' 这种填充值,还不如一开始就写在表定义里。
五、NULL 的等号永远不成立
where phone = NULL 永远给 0 行,where phone is null 才给真正没填的那一行。
原因:NULL 表示「不知道」。
「不知道」等不等于「不知道」?数据库的答案是「不知道」,不算成立。
所以任何跟 NULL 的比较都不会是真。
这一条在写校验时天天碰上:
「查所有没填手机号的人」写成 = NULL,结果永远是空的,还不报错。
排查这种 bug 时最费时间,因为语句本身一点毛病没有。
同理 <> NULL、> NULL 也都不成立,COUNT(列) 也不数 NULL 的行。
NULL 会渗透到每一个运算符里,这是 SQL 三值逻辑的代价。
六、动手 Demo:撞非空、过空串、改 NULL,最后看 NULL 的比较
Demo 五段:不给值撞一次、给空串过一次、update 成 NULL 再撞一次,然后验证非空与默认值搭配,
最后看 = NULL 与 IS NULL 的差别。
第二段是这一篇的核心——它会真的把空字符串插进去,让你看清「非空」这个翻译有多容易误导。
import _q46 as Q
c = Q.blank()
Q.run(c, "CREATE TABLE demo_nn (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
c.execute("INSERT INTO demo_nn (id) VALUES (1)") # 撞非空
Q.run(c, "INSERT INTO demo_nn (id, name) VALUES (2, '')") # 空串能过
Q.show(c, "SELECT id, name, LENGTH(name) AS len FROM demo_nn")
c.execute("UPDATE demo_nn SET name = NULL WHERE id = 2") # 也拦
# NULL 的等号永远不成立
c2 = Q.con()
Q.show(c2, "SELECT COUNT(*) AS with_null FROM students WHERE phone IS NULL")
Q.show(c2, "SELECT COUNT(*) AS wrong FROM students WHERE phone = NULL")
实跑输出:
--------------------------------------------------------------------
第四十四课:非空——空串不是空
--------------------------------------------------------------------
不给值又没默认值,报错
SQL> CREATE TABLE demo_nn (id INTEGER PRIMARY KEY, name TEXT NOT NULL)
完成
SQL> INSERT INTO demo_nn (id) VALUES (1)
报错:NOT NULL constraint failed: demo_nn.name
**报错文字是 `NOT NULL constraint failed: demo_nn.name`**,
**连表名列名都告诉你了。** **MySQL 那边是 `Column 'name' cannot be null`,意思一样。**
空串能过,NULL 不能过
SQL> INSERT INTO demo_nn (id, name) VALUES (2, '')
影响 1 行
SQL> SELECT id, name, LENGTH(name) AS len FROM demo_nn
id name len
---------------------
2 0
(1 行)
**空串进去了,`LENGTH` 是 0。** **非空约束只拦「什么都没有」,不拦「空字符串」。**
**这是一个真实存在的坑**:前端提交了空输入框,
**后端拼成 `''` 传进来,非空约束一句不拦,库里就多了一行「名字是空字符串」的记录。**
**要拦它得加 `CHECK (name <> '')`。**
update 成 NULL 一样拦
SQL> UPDATE demo_nn SET name = NULL WHERE id = 2
报错:NOT NULL constraint failed: demo_nn.name
**非空是列的性质,不管你是插入还是修改,它都管。** **这一点和默认值正相反——默认值只管插入。**
非空与默认值是最佳搭档
SQL> CREATE TABLE demo_nn2 (id INTEGER PRIMARY KEY, status INTEGER NOT NULL DEFAULT 0)
完成
SQL> INSERT INTO demo_nn2 (id) VALUES (1)
影响 1 行
SQL> SELECT id, status FROM demo_nn2
id status
--------------
1 0
(1 行)
**非空保证「这列一定有值」,默认值保证「不给也有值」。**
**两个一起用,业务代码就可以完全不提这一列。**
**只写非空不写默认值**,那每次插入都必须显式给值,漏一次报一次错。
顺便说清:NULL 的等号永远不成立
SQL> SELECT COUNT(*) AS with_null FROM students WHERE phone IS NULL
with_null
----------
1
(1 行)
SQL> SELECT COUNT(*) AS wrong FROM students WHERE phone = NULL
wrong
------
0
(1 行)
**`= NULL` 永远给 0 行**,因为 NULL 表示「不知道」,
**「不知道」等不等于「不知道」?数据库的答案是「不知道」,不算成立。**
**要判断空只能用 `IS NULL`/`IS NOT NULL`。** **这一条在写非空校验时天天碰上**——
**「查所有没填手机号的人」写成 `= NULL`,结果永远是空的,还不报错。**
第一段撞非空,报 NOT NULL constraint failed: demo_nn.name。
第二段空串进去了,LENGTH(name) 是 0——非空不拦空串。
第三段 update 成 NULL,同样被拦,表里还是空串那一行。
第四段非空 + 默认值,只给 id 也有 status = 0。
第五段两个计数:IS NULL 给 1,= NULL 给 0。
30 秒实验
name TEXT NOT NULL的列里,能存进去几种「看起来是空」的值?- 要同时拦住 NULL 和空字符串,建表时怎么写?
SELECT COUNT(*) FROM students WHERE phone = NULL为什么给 0?
常见误区
- 以为
NOT NULL就是「不许空」。 空字符串照样能进,要拦得再加CHECK (列 <> '')。 - 用
= NULL查空值。 永远返回 0 行,还不报错,该用IS NULL。 - 靠程序保证非空。 程序有好几个入口,漏一个就进一行脏数据;数据库约束是最后一道关,不该省。
自测三问(点开对答案)
1. `NOT NULL` 拦得住空字符串吗? —— 拦不住。它只拦 NULL,`''` 是一个有值的字符串,长度为 0。要拦得加 `CHECK (列 <> '')`。 2. 为什么 `= NULL` 永远不成立? —— NULL 表示「不知道」,跟它比较的结果是「不知道」而不是真,所以一行都匹配不上。 3. 非空和默认值分别管什么时候? —— 非空插入修改都管;默认值只管插入,而且只在语句里没提这一列时才生效。有了非空,还缺「自动编号」。 下一篇 9.4 自动递增——一个写错了也不报错的属性。
- ☐ (本篇完,下一篇 9.4 自动递增)