|MySQL使用基础,这么用就对了( 四 )


比如查询参加过学校活动的学生名称:
SELECT NAME FROM student as s where EXISTS(SELECT stu_id FROM student_activities as sa where sa.stu_id=s.id)同样 NOT EXISTS 就是不存在的意思 , 满足为 FALSE , 不满足为 True.
比如查询没有参加过学校活动的学生名称:
SELECT NAME FROM student as s where NOT EXISTS(SELECT stu_id FROM student_activities as sa where sa.stu_id=s.id)集合比较子查询
可以在子查询中 , 使用集合操作符 , 来比较结果 。
|MySQL使用基础,这么用就对了
本文插图

还是上面查询参加学校活动的学生名字的子查询, 同样可以使用 IN:
SELECT name FROM student WHERE id IN (SELECT stu_id FROM student_activities)EXISTS 和 IN 的区别
既然 EXISTS 和 IN 都能实现相同的功能 , 那么他们之间的区别是什么?
现在假设我们有表 A 和 表 B , 其中 A , B 都有字段 cc , 并对 cc 建立了 b+ 索引 , 其中 A 表 n 条记录 , B 表 m 条索引 。
将其模式抽象为:
SELECT * FROM A WHERE cc IN (SELECT cc FROM B) SELECT * FROM A WHERE EXIST (SELECT cc FROM B WHERE B.cc=A.cc)对于 EXISTS 来说 , 会先对外表进行逐条循环 , 每次拿到外表的结果后 , 带入子查询的内表中 , 去判断该值是否存在 。
伪代码类似于下面:
for i in A for j in B if j.cc == i.cc: return result首先先看外表 A , 每一条都需要遍历到 , 所以需要 n 次 。 内表 B , 在查询时由于使用索引进而查询效率变成 log(m) B+ 的树高 , 而不是 m 。
进而总效率:n * log(m)
所以对于 A 表的数量明显小于 B 时 , 推荐使用 EXISTS 查询 。
再看 IN, 会先对内表 B 进行查询 , 然后用外表 A 进行判断 , 伪代码如下:
for i in B for j in A if j.cc == i.cc: return result由于需要首先将内表所有数据查出 , 所以需要的次数就是 m. 再看外表 A, 由于使用了 cc 索引 , 可将 n 简化至 log(n), 也就是 m * log(n).
所以对于 A 表的数据明显大于 B 表时 , 推荐使用 IN 查询 。
总结一下对于 IN 和 EXISTS时 , 采用小表驱动大表的原则 。
这里再扩展下 NOT EXISTS 和 NOT IN 的区别:
SELECT * FROM A WHERE cc NOT IN (SELECT cc FROM B) SELECT * FROM A WHERE NOT EXIST (SELECT cc FROM B WHERE B.cc=A.cc)对于 NOT EXITS 来说 , 和 EXISTS 一样 , 对于内表可以使用 cc 的索引 。 适用于 A 表小于 B 表的情况 。
但对于 NOT IN 来说 , 和 IN 就有区别了 , 由于 cc 设置了索引 cc IN (1, 2, 3) 可以转换成 WHERE cc=1 OR cc=2 OR cc=3 , 是可以正常走 cc 索引的 。 但对于 NOT IN 也就是转化为 cc!=1 OR cc!=2 OR cc!=3 这时由于是不等号查询 , 是无法走索引的 , 进而全表扫描 。
也就是说 , 在设置索引的情况下 NOT EXISTS 比 NOT IN 的效率高 。
但对于没有索引的情况 , IN 和 OR 是不同的:
一、操作不同 1、in:in是把父查询表和子查询表作hash连接 。2、or:or是对父查询表作loop循环 , 每次loop循环再对子查询表进行查询 。二、适用场景不同 1、in:in适合用于子查询表数据比父查询表数据多的情况 。2、or:or适合用于子查询表数据比父查询表数据少的情况 。三、效率不同 1、in:在没有索引的情况下 , 随着in后面的数据量越多 , in的执行效率不会有太大的下降 。2、or:在没有索引的情况下 , 随着or后面的数据量越多 , or的执行效率会有明显的下降 。 总结
这篇文章中主要归纳了一些 SQL 的基础知识:
在使用 SELECT 查询时 , 通过显式指定列名 , 来减少 IO 的传输 , 从而提高效率 。


推荐阅读