什么是NOT EXISTS
通常用来对比两个表,比如要查今日新增的学生数据
其中 NOT EXISTS 可以单独作为一个 WHERE 条件参与查询
SELECT *
FROM student t1
WHERE
pt = '2026-03-09'
AND NOT EXISTS (
SELECT 1
FROM student t2
WHERE pt = '2026-03-08'
AND t2.student_id = t1.student_id
);
NOT EXISTS 与索引
上述SQL执行的次数不会是 student 表行数 ✖ student表行数。有索引的情况下只会执行 student表行数 次
假设 student表行数今日行数为100万 ,昨日行数为98万
场景1:有索引(理想情况)
操作 次数 说明
t1 分区定位 1次 通过 pt 索引直接定位
t1 表扫描 100万次 扫描 t1 的每一行
t2 索引查找 100万次 每行 t1 用 mallId 索引在 t2 中查找
总计 200万次 比暴力扫描快49万倍! ✅
场景2:没有索引(最差情况)
操作 次数 说明
t1 分区定位 1次 通过 pt 索引直接定位
t1 表扫描 100万次 扫描 t1 的每一行
t2 全表扫描 100万 × 98万次 每行 t1 扫描 t2 的所有数据 😱
总计 980亿次 非常慢! ❌
💡 关键优化技术
- 分区裁剪(Partition Pruning)
- WHERE t1.
pt= '2026-03-08' - 只扫描 2026-03-08 分区
- 不扫描其他分区
- 减少数据量
- 索引查找(Index Lookup)
- WHERE t2.mallId = t1.mallId
- 使用 mallId 索引
- 不用全表扫描 t2
- 快速定位
- 短路机制(Short-circuit)
- NOT EXISTS (...)
- 找到一条匹配就立即停止
- 不需要扫描所有匹配项
- 提高效率
可能的执行计划:
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| 1 | SIMPLE | t1 | range | idx_pt | idx_pt | 33 | NULL | 1000 | Using where; Using index |
| 1 | SIMPLE | t2 | ref | idx_mallId | idx_mallId| 8 | t1.mallId | 1 | Using where; Not exists |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
🎯 总结
总共执行次数是多少?
- 有索引:约200万次;【其中一张表的行数】
- 无索引:980亿次【笛卡尔积】