• 数据库(九):多表查询②


    进击のpython

    *****

    数据库——多表查询


    那接着上一节说,其实在真正的操作中,不光是要把数据联合,还需要进行筛选数据,比如:

    打印员工平均年龄大于三十的部门

    拿到一个需求教你怎么写!

    需要员工员工年龄和部门

    员工年龄存放在employee中,部门存放在deparement中

    应该是什么连接呢?根据需求应该是内连接~(这个,你自己理解一下就可以了嘛)

    select * from employee inner join department on employee.dep_id=department.id;
    
    mysql> select * from employee inner join department on employee.dep_id=department.id;
    +----+-----------+--------+------+--------+------+--------------+
    | id | name      | sex    | age  | dep_id | id   | name         |
    +----+-----------+--------+------+--------+------+--------------+
    |  1 | egon      | male   |   18 |    200 |  200 | 技术         |
    |  2 | alex      | female |   48 |    201 |  201 | 人力资源     |
    |  3 | wupeiqi   | male   |   38 |    201 |  201 | 人力资源     |
    |  4 | yuanhao   | female |   28 |    202 |  202 | 销售         |
    |  5 | liwenzhou | male   |   18 |    200 |  200 | 技术         |
    +----+-----------+--------+------+--------+------+--------------+
    5 rows in set (0.00 sec)
    
    

    拿到一个虚拟表,怎么叫虚拟表呢?因为这个表不是硬盘尚存的,而是临时拼在内存里的

    但!他是一张表!

    根据题意应该是部门之间的员工的年龄进行比较,所以就应该分组,按照什么分组呢???

    如果按照id(dep_id),那我select后面拿到的只能是id(dep_id)【参考分组时说的知识点】

    所以就应该用部门的名字name字段,但是发现了吗?有两个name字段,所以应该强制指定:

    select department.name from employee inner join department on employee.dep_id=department.id
    group by department.name;
    

    接着我们要员工年龄的平均值大于三十,是不是应该用having过滤一下:

    select department.name from employee inner join department on employee.dep_id=department.id
    group by department.name
    having avg(age)>30;
    

    为了清晰一点,加个字符串拼接:

    select concat('平均年龄超过三十的部门:',department.name) from employee inner join department on employee.dep_id=department.id
    group by department.name
    having avg(age)>30;
    
    mysql> select concat('平均年龄超过三十的部门:',department.name) from employee inner join department on employee.dep_id=department.id
        -> group by department.name
        -> having avg(age)>30;
    +----------------------------------------------------------------+
    | concat('平均年龄超过三十的部门:',department.name)             |
    +----------------------------------------------------------------+
    | 平均年龄超过三十的部门:人力资源                               |
    +----------------------------------------------------------------+
    1 row in set (2.76 sec)
    
    

    表头不好看,重命名一下:

    select concat('平均年龄超过三十的部门:',department.name) info from employee inner join department on employee.dep_id=department.id
    group by department.name
    having avg(age)>30;
    
    mysql> select concat('平均年龄超过三十的部门:',department.name) info from employee inner join department on employee.dep_id=department.id
        -> group by department.name
        -> having avg(age)>30;
    +--------------------------------------------------+
    | info                                             |
    +--------------------------------------------------+
    | 平均年龄超过三十的部门:人力资源                 |
    +--------------------------------------------------+
    1 row in set (0.00 sec)
    
    

    那这个需求就做出来了

    而至此!select的终极版语法才算是真正的水落石出:

    定义顺序:

    SELECT DISTINCT <select_list>
    FROM <left_table>
    <join_type> JOIN <right_table>
    ON <join_condition>
    WHERE <where_condition>
    GROUP BY <group_by_list>
    HAVING <having_condition>
    ORDER BY <order_by_condition>
    LIMIT <limit_number>
    

    执行顺序:

    (7)     SELECT 
    (8)     DISTINCT <select_list>
    (1)     FROM <left_table>
    (3)     <join_type> JOIN <right_table>
    (2)     ON <join_condition>
    (4)     WHERE <where_condition>
    (5)     GROUP BY <group_by_list>
    (6)     HAVING <having_condition>
    (9)     ORDER BY <order_by_condition>
    (10)    LIMIT <limit_number>
    

    子查询

    除了这种查询方法,还有子查询,子查询就是把查询的结果,当做另一个表的查询条件

    那刚才的例子,也可以这么搞,我先拿到部门平均年龄过30的id,然后再去employee去找对应的部门名

    带in的子查询

    先拿到超过30的id:

    select dep_id from employee inner join department on employee.dep_id=department.id
    group by dep_id
    having avg(age)>30;
    
    mysql> select dep_id from employee inner join department on employee.dep_id=department.id
        -> group by dep_id
        -> having avg(age)>30;
    +--------+
    | dep_id |
    +--------+
    |    201 |
    +--------+
    1 row in set (0.00 sec)
    
    

    拿到了之后进行传值:

    select name from department where id in (
    
    select dep_id from employee inner join department on employee.dep_id=department.id 
    
    group by dep_id 
    
    having avg(age)>30);
    
    mysql> select name from department where id in (
        ->
        -> select dep_id from employee inner join department on employee.dep_id=department.id
        ->
        -> group by dep_id
        ->
        -> having avg(age)>30);
    +--------------+
    | name         |
    +--------------+
    | 人力资源     |
    +--------------+
    1 row in set (0.00 sec)
    
    

    是不是也达到了需求~

    查询技术部员工的姓名怎么写?

    select name from employee where dep_id in (
    select dep_id from employee inner join department on employee.dep_id=department.id
    where department.name = "技术"
    group by dep_id);
    
    mysql> select name from employee where dep_id in (
        -> select dep_id from employee inner join department on employee.dep_id=department.id
        -> where department.name = "技术"
        -> group by dep_id);
    +-----------+
    | name      |
    +-----------+
    | egon      |
    | liwenzhou |
    +-----------+
    2 rows in set (0.00 sec)
    
    

    查看不足1人的部门名这个怎么做?

    这个其实就用到了数学的思想~总数里去掉一人及以上的,是不是就是不足一人的了

    而只要是在用户表里面的用户拥有dep_id的,是不是都是有部门的,去掉这一部分是不是就满足了题意

    select name from department where id not in (select distinct dep_id from employee);

    (子查询得到的是有人的部门id)

    mysql> select name from department where id not in (select distinct dep_id from employee);
    +--------+
    | name   |
    +--------+
    | 运营   |
    +--------+
    1 row in set (0.47 sec)
    
    

    带比较运算符的子查询

    提一句吧,既然拿到的是数据,就像1 12 3 一样的,你就把它看作个数字,当然是可以比较的

    查询大于所有人平均年龄的员工名与年龄

    select avg(age) from employee;查询所有员工的年龄 # 28.000

    那在你心里,此时这一串乱七八糟的东西就是28.000

    此时题意就变成,所有年龄大于28.000的员工名与年龄

    select name,age from employee where age > 28.000;然后将28.000进行“等量代换”

    select name,age from employee where age > (select avg(age) from employee);

    mysql> select name,age from employee where age > (select avg(age) from employee);
    +---------+------+
    | name    | age  |
    +---------+------+
    | alex    |   48 |
    | wupeiqi |   38 |
    +---------+------+
    2 rows in set (0.00 sec)
    
    

    查询大于部门内平均年龄的员工名、年龄

    部门内,一看就是分组,而且应该是以部门进行分组

    select employee.name,employee.age from employee
    inner join 
    (select dep_id,avg(age) avg_age from employee group by dep_id) t2
    on employee.dep_id = t2.dep_id
    where employee.age > t2.avg_age; 
    
    mysql> select employee.name,employee.age from employee
        -> inner join
        -> (select dep_id,avg(age) avg_age from employee group by dep_id) t2
        -> on employee.dep_id = t2.dep_id
        -> where employee.age > t2.avg_age;
    +------+------+
    | name | age  |
    +------+------+
    | alex |   48 |
    +------+------+
    1 row in set (0.34 sec)
    
    

    但是这个employee太长了,所以,给他重新命名比较好(知道为什么是t2了吧)

    select employee.name,employee.age from employee t1
    inner join 
    (select dep_id,avg(age) avg_age from employee group by dep_id) t2
    on t1.dep_id = t2.dep_id
    where t1.age > t2.avg_age; 
    

    带exists关键字的子查询

    理解成if

    select * from employee where exists (select id from department where id=200);

    括号里是不是不为空,那就是真,就相当于if True:于是就有打印结果
    select * from employee where exists (select id from department where id=204);
    括号里是不是为空,那就是假,就相当于if False:于是没有打印结果


    接下来借着这个例子给你讲讲一种思想:我连我自己

    每个职位最新入职的员工

    自己创建表:

    create table employee(
    id int not null unique auto_increment,
    name varchar(20) not null,
    sex enum('male','female') not null default 'male', #大部分是男的
    age int(3) unsigned not null default 28,
    hire_date date not null,
    post varchar(50),
    post_comment varchar(100),
    salary double(15,2),
    office int, #一个部门一个屋子
    depart_id int
    );
    

    插入数据:

    insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values
    ('egon','male',18,'20170301','驻沙河办事处外交大使',7300.33,401,1), #以下是教学部
    ('alex','male',78,'20150302','teacher',1000000.31,401,1),
    ('wupeiqi','male',81,'20130305','teacher',8300,401,1),
    ('yuanhao','male',73,'20140701','teacher',3500,401,1),
    ('liwenzhou','male',28,'20121101','teacher',2100,401,1),
    ('jingliyang','female',18,'20110211','teacher',9000,401,1),
    ('jinxin','male',18,'19000301','teacher',30000,401,1),
    ('成龙','male',48,'20101111','teacher',10000,401,1),
    
    ('歪歪','female',48,'20150311','sale',3000.13,402,2),#以下是销售部门
    ('丫丫','female',38,'20101101','sale',2000.35,402,2),
    ('丁丁','female',18,'20110312','sale',1000.37,402,2),
    ('星星','female',18,'20160513','sale',3000.29,402,2),
    ('格格','female',28,'20170127','sale',4000.33,402,2),
    
    ('张野','male',28,'20160311','operation',10000.13,403,3), #以下是运营部门
    ('程咬金','male',18,'19970312','operation',20000,403,3),
    ('程咬银','female',18,'20130311','operation',19000,403,3),
    ('程咬铜','male',18,'20150411','operation',18000,403,3),
    ('程咬铁','female',18,'20140512','operation',17000,403,3)
    ;
    

    select post,max(hire_date) from employee group by post;

    mysql> select post,max(hire_date) from employee
        ->
        -> group by post;
    +--------------------------------+----------------+
    | post                           | max(hire_date) |
    +--------------------------------+----------------+
    | operation                      | 2016-03-11     |
    | sale                           | 2017-01-27     |
    | teacher                        | 2015-03-02     |
    | 驻沙河办事处外交大使             | 2017-03-01     |
    +--------------------------------+----------------+
    

    这是不是每个部门最新入职的员工,但是,你虽然看到了这张表,但事实上是虚拟的,是不存在的

    所以我们为了让它存在的“合法化”,我们应该将其整体重新命名:

    (select post,max(hire_date) from employee group by post) as t2;

    我们要的是那名员工!

    我们新建的表是不是有职位和日期!employee是不是也有部门和日期,这是不是就联系上了?

    那我们就可以通过这个联系来找到这些员工

    select * from employee  as t1 inner join
    
    (select post,max(hire_date) hire_date from employee group by post) as t2 on
    
    t1.post = t2.post and t1.hire_date=t2.hire_date;
    
    mysql> select * from employee  as t1 inner join
        ->
        -> (select post,max(hire_date) hire_date from employee group by post) as t2 on
        ->
        -> t1.post = t2.post and t1.hire_date=t2.hire_date;
    +----+--------+--------+-----+------------+--------------------------------+--------------+------------+--------+-----------+--------------------------------+------------+
    | id | name   | sex    | age | hire_date  | post                           | post_comment | salary     | office | depart_id | post                           | hire_date  |
    +----+--------+--------+-----+------------+--------------------------------+--------------+------------+--------+-----------+--------------------------------+------------+
    |  1 | egon   | male   |  18 | 2017-03-01 | 驻沙河办事处外交大使           | NULL         |    7300.33 |    401 |         1 | 驻沙河办事处外交大使           | 2017-03-01 |
    |  2 | alex   | male   |  78 | 2015-03-02 | teacher                        | NULL         | 1000000.31 |    401 |         1 | teacher                        | 2015-03-02 |
    | 13 | 格格   | female |  28 | 2017-01-27 | sale                           | NULL         |    4000.33 |    402 |         2 | sale                           | 2017-01-27 |
    | 14 | 张野   | male   |  28 | 2016-03-11 | operation                      | NULL         |   10000.13 |    403 |         3 | operation                      | 2016-03-11 |
    +----+--------+--------+-----+------------+--------------------------------+--------------+------------+--------+-----------+--------------------------------+------------+
    4 rows in set (0.00 sec)
    
    

    当然也可以用where来做,你自己尝试做一下

    select * from employee  as t1 inner join
    (select post,max(hire_date) hire_date from employee group by post) as t2 on
    t1.post = t2.post
    where t1.hire_date=t2.hire_date;
    

    *****
    *****
  • 相关阅读:
    MySQL 8.0系列——轻松改配置,云上友好
    测试expire_logs_days参数
    mongodb单实例安装
    搭建PXC集群指引
    控制mysqldump导出的SQL文件的事务大小
    实战MySQL8.0.17 Clone Plugin
    windows环境下 curl 安装和使用
    git 创建tag , 查看tag , 删除tag
    git 基本操作
    git 一个分支完全覆盖另一个分支
  • 原文地址:https://www.cnblogs.com/jevious/p/11454466.html
Copyright © 2020-2023  润新知