sql中null判断必须用is null而非=,因=null恒返回unknown;in列表超1000项oracle报错,mysql/pg性能骤降;like前导通配符%导致索引失效;字段上用函数如upper()或date()会阻止索引使用。

WHERE 中 NULL 判断不能用 =,必须用 IS NULL
很多新手写 WHERE status = NULL,结果查不到任何数据——因为 SQL 里 = NULL 永远返回 UNKNOWN,不是 TRUE,条件直接失效。
实际场景比如查未填写手机号的用户、订单未确认的记录,都得用 IS NULL 或 IS NOT NULL。
-
WHERE phone IS NULL✅ 正确 -
WHERE phone = NULL❌ 永远不匹配 - 某些方言(如 PostgreSQL)支持
IS NOT DISTINCT FROM做安全比较,但可读性差,日常别硬套
IN 列表超 1000 项时 Oracle 会报错,MySQL 和 PostgreSQL 不限制但性能掉得快
WHERE id IN (1,2,3,...) 看起来干净,但真塞几千个值进去,Oracle 直接抛 ORA-01795: maximum number of expressions in a list is 1000;MySQL 虽不报错,但查询计划容易退化成全表扫描。
常见于后台导出功能、批量状态更新、前端多选后传 ID 列表等场景。
- 拆成多个
IN子句 +UNION ALL(Oracle 必须) - 改用临时表或
VALUES构造集(PostgreSQL 支持WHERE id IN (VALUES (1),(2),...)) - 更稳妥的做法:把 ID 列表写入临时表,再
JOIN,尤其在重复使用时
LIKE 前导通配符(%abc)会让索引失效,除非建函数索引或用全文索引
写 WHERE name LIKE '%admin' 或 '%login%',哪怕 name 字段有索引,也基本等于没用——B-tree 索引只能高效支持前缀匹配('admin%')。
典型场景是模糊搜索框、日志关键词检索、后台“包含某字符串”的筛选。
-
LIKE 'admin%'✅ 走索引 -
LIKE '%admin'❌ 索引失效,走全表/全索引扫描 - MySQL 5.6+ 可考虑
FULLTEXT索引 +MATCH ... AGAINST;PostgreSQL 可用pg_trgm扩展 +GIN索引加速任意位置匹配 - 业务上能改成前缀就尽量改,比如搜索“张”姓用户,用
LIKE '张%'
WHERE 中对字段用函数(如 UPPER(), DATE())会阻止索引使用
写 WHERE UPPER(name) = 'JOHN' 或 WHERE DATE(created_at) = '2024-01-01',即使 name 或 created_at 有索引,优化器也大概率放弃它——因为索引存的是原始值,不是函数计算后的结果。
这在报表定时任务、老系统兼容逻辑、大小写不敏感比对中特别常见。
- 改写为
WHERE name = 'JOHN' OR name = 'john'(小范围可枚举) - 建函数索引:
CREATE INDEX idx_name_upper ON users (UPPER(name))(PostgreSQL / Oracle / MySQL 8.0+ 支持) - 时间字段别用
DATE(),改用范围:WHERE created_at >= '2024-01-01' AND created_at - 注意 collation:如果字段是
utf8mb4_0900_as_cs这类大小写敏感排序,=本身就不区分大小写,根本不用UPPER()
最常被忽略的其实是执行计划验证——光看语句写得“合理”没用,EXPLAIN 一眼就能戳破幻想。哪怕加了索引,WHERE 里一动函数、一加 NULL 判断、一放前导 %,索引就可能悄悄下线。










