《MySQL必知必会》[02] 多表联合查询
1、基本连接
不同类型的数据,存储在多个表中,而所谓多表连接,就是将多个表联合返回一组输出。
1.1 等值连接
基本的连接方式非常简单,只需要在WHERE子句中规定如何关联即可,如下:
SELECT
vend_name,
prod_name,
prod_price
FROM
vendors,
products
WHERE
vendors.vend_id = products.vend_id
ORDER BY
vend_name, prod_name;
11
1
SELECT
2
vend_name,
3
prod_name,
4
prod_price
5
FROM
6
vendors,
7
products
8
WHERE
9
vendors.vend_id = products.vend_id
10
ORDER BY
11
vend_name, prod_name;
最终的输出结果中,会包含另一张表products中的prod_name和prod_price两个列。
在以上的示例中,我们指定了WHERE子句中 vendors.vend_id = products.vend_id ,所以在连接两个表时,你实际上做的是将第一个表中的每一行与第二个表中的每一行配对,WHERE子句作为过滤条件。
这种方式,也叫做等值连接,也称为内部连接(INNER JOIN)。当两个关系不存在公共属性(即字段名称和类型完全相同)时,以取值具有可比性的同类型属性为基础进行连接。
实际上,你也可以用稍微不同的格式来进行,以下和之前示例的效果是一样的:
SELECT
vend_name,
prod_name,
prod_price
FROM
vendors INNER JOIN products ON vendors.vend_id = prodcuts.vend_id;
6
1
SELECT
2
vend_name,
3
prod_name,
4
prod_price
5
FROM
6
vendors INNER JOIN products ON vendors.vend_id = prodcuts.vend_id;
多表连接中对于表连接的数量是没有限制的,规则也都相同,如连接三个表,以上两种方式分别为:
SELECT
vend_name,
prod_name,
prod_price,
quantity
FROM
vendors,
products,
orderitems
WHERE
vendors.vend_id = products.vend_id
AND
orderitems.prod_id = products.prod_id
ORDER BY
vend_name, prod_name;
15
1
SELECT
2
vend_name,
3
prod_name,
4
prod_price,
5
quantity
6
FROM
7
vendors,
8
products,
9
orderitems
10
WHERE
11
vendors.vend_id = products.vend_id
12
AND
13
orderitems.prod_id = products.prod_id
14
ORDER BY
15
vend_name, prod_name;
SELECT
vend_name,
prod_name,
prod_price,
quantity
FROM
(vendors INNER JOIN products ON vendors.vend_id = prodcuts.vend_id)
INNER JOIN orderitems ON orderitems.prod_id = products.prod_id;
9
1
SELECT
2
vend_name,
3
prod_name,
4
prod_price,
5
quantity
6
FROM
7
(vendors INNER JOIN products ON vendors.vend_id = prodcuts.vend_id)
8
INNER JOIN orderitems ON orderitems.prod_id = products.prod_id;
9
1.2 笛卡儿积
由没有连接条件的表关系返回的结果为笛卡儿积。检索出来的行的数目将是第一个表中的行数乘以第二个表中的行数。
SELECT
vend_name,
prod_name,
prod_price
FROM
vendors,
products
ORDER BY
vend_name, prod_name;
9
1
SELECT
2
vend_name,
3
prod_name,
4
prod_price
5
FROM
6
vendors,
7
products
8
ORDER BY
9
vend_name, prod_name;
简单地说,第一个表中的每一行会和第二个表中的每一行一一匹配为新的行筛出。
这种表往往并不是我们所需要的,因为表间关系混乱,每条数据进行了无差别匹配,包括了它从业务逻辑上根本就不应该对应的东西。
所以,在进行表连接时一定要保证WHERE子句。
2、高级联结
2.1 自连接
自连接即两个相同的表的连接,往往作为外部语句用来替代从相同表中检索数据时使用的子查询语句。
如下例为,加入你发现某物品(ID为DTNTR)存在问题,因此想知道该产品供应商的其他产品是否也存在同样的问题:
SELECT
prod_id,
prod_name
FROM
products
WHERE
vend_id =
(SELECT vend_id FROM products WHERE prod_id = 'DTNTR');
8
1
SELECT
2
prod_id,
3
prod_name
4
FROM
5
products
6
WHERE
7
vend_id =
8
(SELECT vend_id FROM products WHERE prod_id = 'DTNTR');
使用自连接的方式:
SELECT
p1.prod_id,
p1.prod_name
FROM
products AS p1,
products AS p2
WHERE
p1.vend_id = p2.vend_id
AND
p2.prod_id = 'DTNTR';
10
1
SELECT
2
p1.prod_id,
3
p1.prod_name
4
FROM
5
products AS p1,
6
products AS p2
7
WHERE
8
p1.vend_id = p2.vend_id
9
AND
10
p2.prod_id = 'DTNTR';
2.2 自然连接 NATURAL JOIN
我们在说到内连接,即等值连接的时候,会返回所有的数据,甚至相同的列多次出现。
而自然连接,就是排除了那些多次出现的列,使每个列只返回一次。即“自然连接”和“内连接”的区别,在于对“重合的相同的部分”处理方式不同:
- "natural join 自然连接" 的处理方式:既然重复了,就丢掉一份,好比distinct
- “inner join 内连接” 的处理方式:虽然重复,但两份都保留
2.3 外部连接 LEFT/RIGHT OUTER JOIN
许多连接是将一个表中的行,与另一个表中的行相关联,某些情况下需要包含没有关联行的行,这时候就要用到外部连接。
简单来说,一般的如自然连接和等值连接,两个表中只有相互匹配的行才会组成新的行放在结果表中,没有匹配的行不论在第一个表还是在第二个表它最终都会信息丢失,不会出现在最终的结果表中。而外部连接,就可以保留这部分信息。
如为了检索所有客户,包括那些没有订单的客户,可以用如下:
SELECT
customers.cust_id,
orders.order_num
FROM
customers LEFT OUTER JOIN orders ON customers.cust_id = orders.cust_id;
5
1
SELECT
2
customers.cust_id,
3
orders.order_num
4
FROM
5
customers LEFT OUTER JOIN orders ON customers.cust_id = orders.cust_id;
使用OUTER JOIN语法时,必须使用RIGHT或LEFT,两者的区别在于,以哪边的表为主。例如左连接,在结果表中,则左边表的行全部保留,而右边表的行则是以是否满足自然连接为依据,不匹配的会丢失。
3、组合查询 UNION
我们大多数的操作,都是从一个或多个表返回数据的单条SELECT语句。
MySQL允许我们执行多条SELECT语句,而把它们的结果作为单个查询结果集返回。这样的组合查询通常称之为并(union)或复合查询(compound query)。
UNION的使用很简单,只需要给出每条SELECT语句,并在各条之间放上关键字UNION。
如下例,需要价格小于等于5的所有物品的列表,而且还想包括供应商1001和1002生产的所有物品(不考虑价格),当然你可以使用WHERE子句来完成,这里我们使用UNION:
(
SELECT
vend_id,
prod_id,
prod_price
FROM
products
WHERE
prod_price <= 5
)
UNION
(
SELECT
vend_id,
prod_id,
prod_price
FROM
products
WHERE
vend_id IN (1001, 1002)
);
21
1
(
2
SELECT
3
vend_id,
4
prod_id,
5
prod_price
6
FROM
7
products
8
WHERE
9
prod_price <= 5
10
)
11
UNION
12
(
13
SELECT
14
vend_id,
15
prod_id,
16
prod_price
17
FROM
18
products
19
WHERE
20
vend_id IN (1001, 1002)
21
);
当然,如果用WHERE子句的话,方式如下:
SELECT
vend_id,
prod_id,
prod_price
FROM
products
WHERE
prod_price <= 5
OR
vend_id IN (1001, 1002);
1
SELECT
2
vend_id,
3
prod_id,
4
prod_price
5
FROM
6
products
7
WHERE
8
prod_price <= 5
9
OR
10
vend_id IN (1001, 1002);
在这个例子中,UNION可能使用起来更复杂,但是对于更复杂的过滤条件,或者从多个表(而不是单个表)中检索数据的情形,使用UNION可能会使处理更简单,逻辑更清晰。
另,使用UNION有几条规则需要注意:
- UNION必须由两条或以上的SELECT语句组成
- 每个查询必须包含相同的列、表达式或聚集函数
- 列数据类型必须兼容
- UNION会默认自动取消各SELECT结果中重复多余的结果,如果需要保留,使用UNION ALL关键字
- 结果集无法出现一部分使用一种方式排序,另一部分使用其他方式排序,只能使用一条ORDER BY