应使用 exists 而非 count() 判断存在性,因 exists 找到首行即止,语义精准、开销小;count() 会全表扫描且可能引发 null 异常。

用 EXISTS 而不是 COUNT(*)
查“有没有”,别用 COUNT(*) ——它会统计全部匹配行,哪怕只有一条也得扫完所有结果。而 EXISTS 在找到第一条就停,语义精准、开销极小。
常见错误写法:SELECT COUNT(*) > 0 FROM users WHERE email = 'x@y.z',这不仅多算,还可能因空结果触发 NULL 比较异常(尤其在严格模式下)。
- 正确写法:
SELECT EXISTS(SELECT 1 FROM users WHERE email = 'x@y.z') - 子查询里必须用
SELECT 1,不是SELECT *或SELECT email,避免字段解析和投影开销 - 如果只是判断表是否非空(不带条件),直接写
SELECT EXISTS(SELECT 1 FROM table_name)
WHERE 条件必须走索引
EXISTS 快的前提是子查询能快速定位——否则照样全表扫描。主键或唯一索引字段(如 id)基本没问题;但普通字段(如 email、status)若没索引,EXISTS 也救不了。
检查方式:
- MySQL:
SHOW INDEX FROM users WHERE Column_name = 'email' - PostgreSQL:
\d users,看是否有对应索引项 - 无索引又高频查询?补一个:
CREATE INDEX idx_users_email ON users(email) - 复合条件(如
WHERE email = ? AND status = 'active')建议建联合索引,顺序按等值字段优先(email, status)
注意数据库返回值类型差异
不同数据库对 EXISTS 的返回值处理不一致,应用层取值前必须确认驱动如何映射:
- PostgreSQL:返回原生
boolean,值为TRUE/FALSE - MySQL 5.7:返回
TINYINT(1),值为1或0 - MySQL 8.0+:支持
BOOLEAN别名,但底层仍是TINYINT - SQL Server:返回
BIT,值为1或0
别在代码里硬写 if result == True,Python 的 mysqlclient 就会把 MySQL 的 1 当整数返回,不是布尔。
别在 EXISTS 里加 FOR UPDATE
有人想“顺手锁住”,于是在子查询里写 SELECT 1 FROM users WHERE id = 123 FOR UPDATE。这会导致:
- 即使只验证存在性,也会持行锁甚至间隙锁
- 阻塞其他事务对该行的读写,放大并发冲突
- 锁范围可能超出预期,尤其在可重复读隔离级别下
除非你接下来**立刻执行 UPDATE**,否则不要加。真要原子读-改,直接用带条件的 UPDATE ... WHERE id = ? AND status = 'pending' 并检查影响行数更安全。
索引是否生效、返回值类型适配、锁意图是否合理——这三个点漏掉任一个,“快速判断”就可能变成隐性瓶颈。










