LabHub
学习 学习路径 课程

SQL 实战

查询与过滤 — 三值逻辑与条件的顺序

在 LabHub 中继续学习

一句话总结

SQL的WHERE只通过真值的行,如果NULL混在一起,结果既不是真也不是假,而是未知,所以在使用条件时,一定要牢记第三个值。

概念图: 包括两端值。 · 顺序即优先顺序 · 排序必须始终包括唯一值,并进行tie-break。 · 真·假·不知道

为什么需要这个?

大多数编程语言只处理真和假两个值。SQL处理三个值。真、假和未知。NULL参与的所有比较都变成未知,WHERE只通过真行的值,所以未知像假一样被处理。

这个差异在实际工作中会造成一个安静的错误。例如,当条件被颠倒时,行数之和与总和不符的现象就是一个典型的例子。

SELECT count(*) FROM customers WHERE city = '서울';       -- 48
SELECT count(*) FROM customers WHERE city <> '서울';      -- 335
-- 합이 383 인데 전체는 400 이다. 나머지 17 은 city 가 NULL 인 행이다.

<>因为罗也无法过滤,所以NULL行在双方任何地方都没有。这时需要的是IS DISTINCT FROM是的。这个运算符将NULL与一个值进行比较,所以在上述例子中返回352。

怎么行动

整理经常使用的条件的性格的话是这样的。

条件 意义 注意事项
BETWEEN a AND b a 以上 b 以下 包括两端值。 如果在日期范围内使用,可能会遗漏最后一天的午夜之后。
IN (...) 列表中之一 列表中有NULLNOT IN使用的话结果会全部消失
LIKE 'kim%' 前缀一致 可搜索索引
LIKE '%kim%' 部分匹配 因为不知道起始点,所以无法用普通索引进行搜索
IS NULL 没有价格 = NULL银总是0行
COALESCE(a, b) 如果a为NULL,则用作b 表示用替换,如果用作条件,则无法访问索引。

CASE从上面开始选择第一个符合条件的项目。因此,列出重叠条件时,顺序即优先顺序。这是因为在划分金额区间时,从较大的值开始使用是惯例的原因。

也需要指出页码。ORDER BY没有LIMIT不能保证只要输入就会出现什么行。而且如果排序标准有平分,那么在页面之间会出现行重复或遗漏。**排序必须始终包括唯一值,并进行tie-break。**在实际工作中ORDER BY created_at DESC, id DESC最后像这样附加基本键。

在现场相遇的样子

OFFSET因为方式的页码翻页随着后面的页面越多就越慢。OFFSET 10000是读完最后一行后丢弃的。替代方案是以最后看到的值为基准继续读取的游标方式。条件是WHERE (created_at, id) < (마지막값, 마지막id)变成形状后,通过索引搜索直接找到开始点。

NULL 创造的三个值逻辑

SQL的比较不是真·假,而是真·假·不知道。这就是条件句的运行。 换。

NULL = NULL      → UNKNOWN (참이 아니다)
NULL <> 1        → UNKNOWN
NULL IS NULL     → TRUE      ← 이것만 참이 된다

WHERE只留下真善美。UNKNOWN像谎言一样被抛弃。所以这样的 发生事情了。

-- status 가 NULL 인 행은 두 질의 어디에도 안 나온다
select * from orders where status = 'done';
select * from orders where status <> 'done';

要处理整个内容,必须指定NULL。

where status is distinct from 'done'    -- NULL 도 "다르다" 로 본다
where status <> 'done' or status is null
where coalesce(status, '') <> 'done'

IS DISTINCT FROM这个是最干净的。将NULL比作一个值

统计上也不同。count(*)虽然只算行count(col)只有不是NULL的 接受。avg·sum也忽略NULL,如果要将缺失视为0的话coalesce 必须填写。

条件的顺序(一般)不会改变性能。

WHERE a = 1 AND b = 2即使在更改顺序,优化器也会自行决定。人 要关注的不是顺序,而是可以使用索引的形式

-- ❌ 인덱스를 못 쓴다 — 컬럼에 함수를 씌웠다
where date(created_at) = '2026-09-06'
where upper(email) = 'A@B.COM'

-- ✅ 범위로 바꾸거나 표현식 인덱스를 만든다
where created_at >= '2026-09-06' and created_at < '2026-09-07'
create index on users ((upper(email)));

LIKE也是如此。'abc%'虽然使用索引'%abc'不能写。 如果需要从后面查找的话,请写trigram索引或专业搜索。

至少阅读执行计划

explain (analyze, buffers) select …;

analyze实际上反过来看,buffers显示了读了多少。要看的 是三个。

下次实习要做的事情

在实际的电商模式中,依次使用BETWEEN、IN、LIKE、COALESCE、CASE、IS DISTINCT FROM。特别是在混合了NULL的列中,当反转条件时,会亲自体验行数不符的经历。