zoukankan      html  css  js  c++  java
  • 05-多表查询

    多表查询

    1.说明

      当存储时,相同数据出现多次决不是一件好事,这个因素是关系数据库设计的基础。关系表的设计就是要保证把信息分解成多个表,一类数据一个表,各表通过默写常用的值互相关联。

      分解数据为多个表能更有效地存储,更方便地处理,并且具有更大的可伸缩性。但这样就不能使用单条SELECT语句检索出数据。

      SQL最强大的功能之一就是能在数据检索查询的执行中连接(join)表。

    常见术语:

      外键(foreign key)外键为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。

      可伸缩性(scale)能够适应不断增加的工作量而不失败。设计良好的数据库或应用程序称之为可伸缩性好(sacle well)。

    数据源

    #建表
    create table department(
    id int,
    name varchar(20) 
    );
    
    create table employee(
    id int primary key auto_increment,
    name varchar(20),
    sex enum('male','female') not null default 'male',
    age int,
    dep_id int
    );
    
    #插入数据
    insert into department values
    (200,'技术'),
    (201,'人力资源'),
    (202,'销售'),
    (203,'运营');
    
    insert into employee(name,sex,age,dep_id) values
    ('egon','male',18,200),
    ('alex','female',48,201),
    ('wupeiqi','male',38,201),
    ('yuanhao','female',28,202),
    ('liwenzhou','male',18,200),
    ('jingliyang','female',18,204)
    ;
    
    
    #查看表结构和数据
    mysql> desc department;
    +-------+-------------+------+-----+---------+-------+
    | Field | Type | Null | Key | Default | Extra |
    +-------+-------------+------+-----+---------+-------+
    | id | int(11) | YES | | NULL | |
    | name | varchar(20) | YES | | NULL | |
    +-------+-------------+------+-----+---------+-------+
    
    mysql> desc employee;
    +--------+-----------------------+------+-----+---------+----------------+
    | Field | Type | Null | Key | Default | Extra |
    +--------+-----------------------+------+-----+---------+----------------+
    | id | int(11) | NO | PRI | NULL | auto_increment |
    | name | varchar(20) | YES | | NULL | |
    | sex | enum('male','female') | NO | | male | |
    | age | int(11) | YES | | NULL | |
    | dep_id | int(11) | YES | | NULL | |
    +--------+-----------------------+------+-----+---------+----------------+
    
    mysql> select * from department;
    +------+--------------+
    | id | name |
    +------+--------------+
    | 200 | 技术 |
    | 201 | 人力资源 |
    | 202 | 销售 |
    | 203 | 运营 |
    +------+--------------+
    
    mysql> select * from employee;
    +----+------------+--------+------+--------+
    | id | name | sex | age | dep_id |
    +----+------------+--------+------+--------+
    | 1 | egon | male | 18 | 200 |
    | 2 | alex | female | 48 | 201 |
    | 3 | wupeiqi | male | 38 | 201 |
    | 4 | yuanhao | female | 28 | 202 |
    | 5 | liwenzhou | male | 18 | 200 |
    | 6 | jingliyang | female | 18 | 204 |
    +----+------------+--------+------+--------+
    
    表department与employee
    自https://www.cnblogs.com/Eva-J/articles/9688383.html

    2.建立连接

    2.1. 交叉连接:生成笛卡儿积

      笛卡儿积(cartesian product)由没有连接条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。

    范例:

    mysql> select * from employee,department;
    +----+------------+--------+------+--------+------+--------------+
    | id | name       | sex    | age  | dep_id | id   | name         |
    +----+------------+--------+------+--------+------+--------------+
    |  1 | egon       | male   |   18 |    200 |  200 | 技术         |
    |  1 | egon       | male   |   18 |    200 |  201 | 人力资源     |
    |  1 | egon       | male   |   18 |    200 |  202 | 销售         |
    |  1 | egon       | male   |   18 |    200 |  203 | 运营         |
    |  2 | alex       | female |   48 |    201 |  200 | 技术         |
    |  2 | alex       | female |   48 |    201 |  201 | 人力资源     |
    |  2 | alex       | female |   48 |    201 |  202 | 销售         |
    |  2 | alex       | female |   48 |    201 |  203 | 运营         |
    |  3 | wupeiqi    | male   |   38 |    201 |  200 | 技术         |
    |  3 | wupeiqi    | male   |   38 |    201 |  201 | 人力资源     |
    |  3 | wupeiqi    | male   |   38 |    201 |  202 | 销售         |
    |  3 | wupeiqi    | male   |   38 |    201 |  203 | 运营         |
    |  4 | yuanhao    | female |   28 |    202 |  200 | 技术         |
    |  4 | yuanhao    | female |   28 |    202 |  201 | 人力资源     |
    |  4 | yuanhao    | female |   28 |    202 |  202 | 销售         |
    |  4 | yuanhao    | female |   28 |    202 |  203 | 运营         |
    |  5 | liwenzhou  | male   |   18 |    200 |  200 | 技术         |
    |  5 | liwenzhou  | male   |   18 |    200 |  201 | 人力资源     |
    |  5 | liwenzhou  | male   |   18 |    200 |  202 | 销售         |
    |  5 | liwenzhou  | male   |   18 |    200 |  203 | 运营         |
    |  6 | jingliyang | female |   18 |    204 |  200 | 技术         |
    |  6 | jingliyang | female |   18 |    204 |  201 | 人力资源     |
    |  6 | jingliyang | female |   18 |    204 |  202 | 销售         |
    |  6 | jingliyang | female |   18 |    204 |  203 | 运营         |
    +----+------------+--------+------+--------+------+--------------+
    24 rows in set (0.01 sec) 

    2.2. 内部连接:只连接匹配的行

    建立的连接基于两个表之间的相等测试,称之为等值连接(equijoin),也称之为内部连接。

    语法

    SELECT 表名1.字段名1, 表名1.字段名2, 表名1.字段名3, 表名2.字段名1 FROM 表名1 INNER JOIN 表名2 ON  表名1.字段=表名2.字段;

    这里两个表之间的关系是FROM子句的组成部分,以INNER JOIN指定。在使用这种语法时,连接条件用特定的ON子句而不是WHERE子句给出。传递给ON的实际条件与传递给WHERE的相同。

    范例

    # 找出两张表共有的字段,利用条件从笛卡儿积结果中筛选除了正确的结果。
    mysql> select employee.id,employee.name,employee.age,employee.sex,department.name dep_name from employee inner join department on employee.dep_id=department.id; +----+-----------+------+--------+--------------+ | id | name | age | sex | dep_name | +----+-----------+------+--------+--------------+ | 1 | egon | 18 | male | 技术 | | 2 | alex | 48 | female | 人力资源 | | 3 | wupeiqi | 38 | male | 人力资源 | | 4 | yuanhao | 28 | female | 销售 | | 5 | liwenzhou | 18 | male | 技术 | +----+-----------+------+--------+--------------+ 5 rows in set (0.00 sec) mysql> select employee.id,employee.name,employee.age,employee.sex,department.name dep_name from employee, department where employee.dep_id=department.id; +----+-----------+------+--------+--------------+ | id | name | age | sex | dep_name | +----+-----------+------+--------+--------------+ | 1 | egon | 18 | male | 技术 | | 2 | alex | 48 | female | 人力资源 | | 3 | wupeiqi | 38 | male | 人力资源 | | 4 | yuanhao | 28 | female | 销售 | | 5 | liwenzhou | 18 | male | 技术 | +----+-----------+------+--------+--------------+ 5 rows in set (0.00 sec)

    2.3 外部连接

    连接中包含了在相关表中没有关联行的行

    外部连接的类型

      存在两种基本的外部连接形式:左外部连接和右外部连接。他们之间的唯一差别时所关联的表的顺序不同。换句话说,左外部连接可通过颠倒FROM或WHERE子句中表的顺序转换为右外部连接。因此,两种类型的外部连接可互换使用,而究竟使用哪一种纯粹是根据方便而定。

    2.3.1. 外部连接之左连接

    以左表为准,优先显示左表全部记录

    语法

    SELECT 表名1.字段名1, 表名1.字段名2, 表名1.字段名3, 表名2.字段名1 FROM 表名1 LEFT JOIN 表名2 ON  表名1.字段=表名2.字段;

    范例

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

    2.3.2. 外部连接之右连接

    以右表为基准,优先显示右表全部记录

    语法

    SELECT 表名1.字段名1, 表名1.字段名2, 表名1.字段名3, 表名2.字段名1 FROM 表名1 RIGHT JOIN 表名2 ON  表名1.字段=表名2.字段;

    范例

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

    2.3.3. 外部连接

    显示左右两个表的全部记录,及左连接和右连接的合集

    语法

    SELECT * FROM 表名1 LEFT JOIN 表名2 ON 表名1.字段1=表名2.字段2 UNION SELECT * FROM 表名1 RIGHT JOIN 表名2 ON 表名1.字段1=表名2.字段2 ;

    范例

    mysql> select * from employee left join department on employee.dep_id=department.id union select * from employee right join department on employee.dep_id=department.id;
    +------+------------+--------+------+--------+------+--------------+
    | id   | name       | sex    | age  | dep_id | id   | name         |
    +------+------------+--------+------+--------+------+--------------+
    |    1 | egon       | male   |   18 |    200 |  200 | 技术         |
    |    5 | liwenzhou  | male   |   18 |    200 |  200 | 技术         |
    |    2 | alex       | female |   48 |    201 |  201 | 人力资源     |
    |    3 | wupeiqi    | male   |   38 |    201 |  201 | 人力资源     |
    |    4 | yuanhao    | female |   28 |    202 |  202 | 销售         |
    |    6 | jingliyang | female |   18 |    204 | NULL | NULL         |
    | NULL | NULL       | NULL   | NULL |   NULL |  203 | 运营         |
    +------+------------+--------+------+--------+------+--------------+
    7 rows in set (0.01 sec)

    2.4. 连接查询

    如果过滤条件的字段在两个表中都存在,需要加上表名

    语法

    SELECT 表名1.字段名1, 表名1.字段名2, 表名1.字段名3, 表名2.字段名1 FROM 表名1 INNER JOIN 表名2 ON 表名1.字段=表名2.字段 WHERE 过滤条件...;

    SELECT 表名1.字段名1, 表名1.字段名2, 表名1.字段名3, 表名2.字段名1 FROM 表名1 LEFT JOIN 表名2 ON 表名1.字段=表名2.字段 WHERE 过滤条件...;

    SELECT 表名1.字段名1, 表名1.字段名2, 表名1.字段名3, 表名2.字段名1 FROM 表名1 RIGHT JOIN 表名2 ON 表名1.字段=表名2.字段 WHERE 过滤条件...;

    SELECT * FROM 表名1 LEFT JOIN 表名2 ON 表名1.字段1=表名2.字段2 UNION SELECT * FROM 表名1 RIGHT JOIN 表名2 ON 表名1.字段1=表名2.字段2 WHERE 过滤条件...;

    范例

    ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'wher' at line 1
    mysql> select employee.id,employee.name,department.name as depart_name from employee inner join department on employee.dep_id=department.id where name regexp 'li';
    ERROR 1052 (23000): Column 'name' in where clause is ambiguous
    mysql> select employee.id,employee.name,department.name as depart_name from employee inner join department on employee.dep_id=department.id where employee.name regexp 'li';
    +----+-----------+-------------+
    | id | name      | depart_name |
    +----+-----------+-------------+
    |  5 | liwenzhou | 技术        |
    +----+-----------+-------------+
    1 row in set (0.00 sec)

    3.子查询

    查询(query)任何SQL语句都是查询。但此术语一般指SELECT语句。

    子查询(subquery)即嵌套在其他查询中的查询

    特点

      1- 子查询是将一个查询语句嵌套在另一个查询语句中。

      2- 内层查询语句的查询结果,可以为外层查询语句提供查询条件。

      3- 子查询中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等关键字

      4- 还可以包含比较运算符:= 、 !=、> 、<等

    3.1 带关键字IN的子句

    语法

    SELECT 字段 FROM 表名1 WHERE 字段1 IN (SELECT 字段2 FROM 表名2);

    范例

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

    3.2 带运算符的子查询

    语法

    SELECT 字段 FROM 表名1 WHERE 字段1 比较运算符 (SELECT 字段2 FROM 表名2);

    范例

    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)

    3.3 带EXISTS关键字的子查询

    当子查询语句为真的时候,执行外层查询语句。反之,则不进行外部查询语句。

    语法

    SELECT 字段 FROM 表名1 EXISTS (子查询语句);

    范例

    # 子查询语句为假,不执行外层查询语句
    mysql> select * from employee where exists (select id from department where id=205); Empty set (0.00 sec)
    # 子查询语句为真,执行外层查询语句 mysql> select * from employee where exists (select id from department where id=201); +----+------------+--------+------+--------+ | id | name | sex | age | dep_id | +----+------------+--------+------+--------+ | 1 | egon | male | 18 | 200 | | 2 | alex | female | 48 | 201 | | 3 | wupeiqi | male | 38 | 201 | | 4 | yuanhao | female | 28 | 202 | | 5 | liwenzhou | male | 18 | 200 | | 6 | jingliyang | female | 18 | 204 | +----+------------+--------+------+--------+ 6 rows in set (0.00 sec)
  • 相关阅读:
    Ugly Numbers
    Present
    Out of Hay
    Stars(树状数组或线段树)
    Humble Numbers
    Wireless Network
    Rank List
    Argus
    食物链
    Antenna Placement
  • 原文地址:https://www.cnblogs.com/gongniue/p/10561451.html
Copyright © 2011-2022 走看看