SQL基础-子查询&EXISTS&UNION

一、子查询

1、使用子查询作为计算字段

子查询:嵌套在其他查询中的查询

现在有两个表,student表和teacher表

创建teacher表,并插入数据:
CREATE TABLE `teacher` (
  `teacher_id` varchar(255) DEFAULT NULL COMMENT '老师编号',
  `teacher_name` varchar(255) DEFAULT NULL COMMENT '老师姓名',
  `gender` varchar(255) DEFAULT NULL COMMENT '性别'
) ENGINE=MyISAM DEFAULT CHARSET=gbk COMMENT='老师';

INSERT INTO `teacher` VALUES 
('T0001','高齐妍',''),('T0002','李红',''),
('T0003','李一萱',NULL),('T0004','刘金霞',''),('T0005','刘思哲',''),
('T0006','刘兆祥',''),('T0007','刘哲宇',''),('T0008','梅艺涵',''),
('T0009','梅姿君',''),('T0010','牛雨',''),('T0011','牛光滢',''),
('T0012','黄雅',''),('T0013','任筱',''),('T0014','吴静婷',''),
('T0015','习芸颍',''),('T0016','叶惠燕',''),('T0017','周纯',''),
('T0018','周圣杰',''),('T0019','方焓',''),('T0020','方杰萍','');


比如:
    如何同时查询出学生编号、学生姓名、老师编号、老师姓名?
    SELECT 
    student_id,
    student_name,
    teacher_id,
    (
        SELECT teacher_name
        FROM teacher 
        -- 使用表名消除字段歧义
        WHERE teacher.teacher_id = student.teacher_id
    )
    FROM student;

    #可以为表指定别名
    SELECT 
    student_id,
    student_name,
    teacher_id,
    (
        SELECT teacher_name
        FROM teacher b
        WHERE b.teacher_id = a.teacher_id
    )
    FROM student a;


此时子查询不能返回多条记录;


2、使用子查询过滤数据(IN)

比如:
    如何获取姓牛的老师教了哪些学生?
    SELECT
    student_id,student_name
    FROM student
    WHERE teacher_id IN ( SELECT teacher_id FROM teacher
    WHERE teacher_name like '牛%');


in 后面是一个集合;


3、IN和EXISTS比较

existsin 最大的区别在于 in引导的子句只能返回一个字段;

exists强调的是是否返回结果集,不要求知道返回什么;

exists 是用来判断是否存在的,当exists(查询)中的查询存在结果时则返回真否则返回假。not exists则相反。

EXISTS只返回TRUE或FALSE,不会返回UNKNOWN。

IN当遇到包含NULL的情况,那么就会返回UNKNOWN。

exists做为where条件时,是先对where前的主查询询进行查询,然后用主查询的结果一个一个的代入exists的查询进行判断,
如果为真则输出当前这一条主查询的结果,否则不输出。


select * from A where id in(select id from B)

in()适合B表比A表数据小的情况;

exists()适合B表比A表数据大的情况;

当A表数据与B表数据一样大时,in与exists效率差不多,可任选一个使用;


4、使用子查询过滤数据(EXISTS)

比如:
    如何获取姓牛的老师教了哪些学生?

    SELECT
    student_id,student_name
    FROM student a
    -- 这里的select 1没有具体意义,写成select 2也可以;
    WHERE EXISTS ( SELECT 1 FROM teacher b
    WHERE a.teacher_id = b.teacher_id
    AND b.teacher_name like '牛%'
    );


##NO EXISTS

比如:
    如何获取除姓牛的老师之外的其他老师教了哪些学生?
    SELECT
    student_id,student_name
    FROM student a
    WHERE NOT EXISTS ( SELECT 1 FROM teacher b
    WHERE a.teacher_id = b.teacher_id
    AND b.teacher_name like '牛%'
    );




二、UNION

1、UNION 和UNION ALL

##
比如:
    如何同时查询出年龄为10岁或一年级一班的所有学生?

    SELECT * FROM student WHERE age = 10
    UNION ALL
    SELECT * FROM student WHERE class_id = 'G0101';


比如:
    如何同时查询出年龄为10岁或一年级一班的所有学生(去除重复)?

    SELECT * FROM student WHERE age = 10
    UNION
    SELECT * FROM student WHERE class_id = 'G0101';


UNION ALLUNION:
    相同点:都是用来合并多个结果集;

    不同点:UNION ALL合并结果集后不去除重复记录;
          UNION合并结果集后去除重复记录;


2、合并2个以上的结果集

比如:
    如何同时查询出年龄为10岁或一年级一班或性别为男的所有学生?

    SELECT * FROM student WHERE age = 10
    UNION
    SELECT * FROM student WHERE class_id = 'G0101'
    UNION
    SELECT * FROM student WHERE gender = '';


3、合并来源于不同的表的结果集

比如:
    如何同时查询出所有的学生编号、学生姓名和老师编号、老师姓名?

    SELECT student_id,student_name FROM student
    UNION
    SELECT teacher_id,teacher_name FROM teacher;


4、不同结果集什么情况下可以合并

需要注意:
     待合并的结果集的字段数量必须一致。

错误写法(字段数量不一致):        
    SELECT student_id,student_name,age FROM student
    UNION
    SELECT teacher_id,teacher_name FROM teacher;


其他注意的几点:
     合并后的结果集的title与第一个结果集保持一致。
     待合并的结果集的字段顺序、字段类型的大类及字段值的含义尽量保持一致。

如下,虽然不会报错,但是也应该尽量避免不一样的字段在同一列:
    SELECT student_id,student_name,age FROM student
    UNION
    SELECT teacher_id,teacher_name,gender FROM teacher;


以上注意事项,UNION和UNION ALL都适用;


5、UNION ALL与UNION混用

比如:
    如何同时查询出年龄为10岁或一年级一班(前面两个结果集需要去除重复)或性别为男(合并时不去除重复)的所有学生?

    SELECT * FROM student WHERE age = 10
    UNION
    SELECT * FROM student WHERE class_id = 'G0101'
    UNION ALL
    SELECT * FROM student WHERE gender = '';


需要注意的几点:
     UNION ALL与UNION的执行优先级一致,谁在前谁先执行;
     不可以使用括号改变执行优先级;


不建议UNION ALL与UNION混用;


6、合并后的结果集排序

比如:
    如何同时查询出年龄为10岁或一年级一班的所有学生(按姓名升序排序)?

    SELECT * FROM student WHERE age = 10
    UNION ALL
    SELECT * FROM student WHERE class_id = 'G0101'
    ORDER BY student_name;


会先执行UNION ALL,再执行ORDER BY


7、union all&or&in的使用

union all 也不一定就比 or及in 快,要结合实际情况分析到底使用哪种情况。

对于索引列来最好使用union all,因复杂的查询【包含运算等】将使or、in放弃索引而全表扫描,除非你能确定or、in会使用索引;

对于只有非索引字段来说你就老老实实的用or 或者in,因为 非索引字段本来要全表扫描而union all 只成倍增加表扫描的次数;

对于既有索引字段【索引字段有效】又包含非索引字段来时,按理你也使用or 、in或者union all 都可以,但是我推荐使用or、in。

以上主要针对的是单表,而多表联合查询来说,考虑的地方就比较多了,比如连接方式,查询表数据量分布、索引等,再结合单表的策略选择合适的关键字;
posted @ 2019-08-30 10:56  米兰的小铁將  阅读(1080)  评论(0编辑  收藏  举报