SQL学习(联结表)

联结

SQL最强大的功能之一就是在数据检索查询的执行中联结(join)表。联结是利用SQL的SELECT能执行的最重要的操作。

关系表

假如有一个包含产品目录的数据库表,其中每种类别的物品占一行。对于每种木品要存储的信息包括产品描述和价格,以及生产该产品的供应商消息。

现在,假如有由同一供应商生产的多种物品,那么何处存储供应商信息(如,供应商名、地址、联系方法等)呢?将这些数据与产品信息分开存储的理由如下。

  • 因为同一供应商生产的每个产品的供应商的信息都是相同的,对每个产品重复此信息既浪费时间又浪费存储空间。
  • 如果供应商信息改变(例如,供应商搬家或电话号码变动),只需改动一次即可。
  • 如果有重复数据,很难保证每次输入该数据的方式都相同。不一致的数据在报表中很难利用。
    关键是,相同数据出现多次决不是一件好事,此因素是关系数据库设计的基础。关系表的设计就是要保证把信息分解成多个表,一类数据一个表。各表通过某些常用的值互相关联。
    在这个例子中,可以建立两个表,一个用来存储供应商表,另一个存储产品信息。Vendors表包含所有供应商信息,每个供应商占一行,每个供应商具有唯一标识。此标识称为主键,可以是供应商ID或任何其他唯一值。
    product表只存储产品信息,它除了供应商ID外不存储其他供应商信息。vendors的主键又叫作products的外键,它将vendors表与products表关联,利用供应商ID能从vendors表中找出相应供应商的详细信息。
    外键:外键为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。

关系数据可以有效地存储和方便地处理。因此,关系数据库的可伸缩性远比非关系数据库要好。

联结是一种机制,用来在一条SELECT语句中关联表,因此称之为联结。

创建联结

mysql> SELECT vend_name, prod_name, prod_price FROM vendors,products WHERE vendors.vend_id = products.vend_id ORDER BY vend_name, prod_name;
+-------------+----------------+------------+
| vend_name   | prod_name      | prod_price |
+-------------+----------------+------------+
| ACME        | Bird seed      |      10.00 |
| ACME        | Carrots        |       2.50 |
| ACME        | Detonator      |      13.00 |
| ACME        | Safe           |      50.00 |
| ACME        | Sling          |       4.49 |
| ACME        | TNT (1 stick)  |       2.50 |
| ACME        | TNT (5 sticks) |      10.00 |
| Anvils R Us | .5 ton anvil   |       5.99 |
| Anvils R Us | 1 ton anvil    |       9.99 |
| Anvils R Us | 2 ton anvil    |      14.99 |
| Jet Set     | JetPack 1000   |      35.00 |
| Jet Set     | JetPack 2000   |      55.00 |
| LT Supplies | Fuses          |       3.42 |
| LT Supplies | Oil can        |       8.99 |
+-------------+----------------+------------+
14 rows in set (0.00 sec)

FROM子句中可以看出,与以前的SELECT语句不一样,这条语句的FROM子句列出了两个表,分别是vendors和products。它们就是这条SELECT语句联结的两个表的名字,这两个表用WHERE子句正确联结。

内部联结

目前为止所有的联结称为等值联结,它基于两个表之间的相等测试。这种联结称为内部联结。其实,对于这种联结可以使用稍微不同的语法来明确指定联结的类型。

mysql> SELECT prod_name, vend_name, prod_price FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id;
+----------------+-------------+------------+
| prod_name      | vend_name   | prod_price |
+----------------+-------------+------------+
| .5 ton anvil   | Anvils R Us |       5.99 |
| 1 ton anvil    | Anvils R Us |       9.99 |
| 2 ton anvil    | Anvils R Us |      14.99 |
| Fuses          | LT Supplies |       3.42 |
| Oil can        | LT Supplies |       8.99 |
| Detonator      | ACME        |      13.00 |
| Bird seed      | ACME        |      10.00 |
| Carrots        | ACME        |       2.50 |
| Safe           | ACME        |      50.00 |
| Sling          | ACME        |       4.49 |
| TNT (1 stick)  | ACME        |       2.50 |
| TNT (5 sticks) | ACME        |      10.00 |
| JetPack 1000   | Jet Set     |      35.00 |
| JetPack 2000   | Jet Set     |      55.00 |
+----------------+-------------+------------+
14 rows in set (0.00 sec)

这两个表之间的关系是FROM子句的组成部分,以INNER JOIN指定。联结条件用特定的ON子句而不是WHERE子句给出。传递给ON的实际条件与传递给WHERE的相同。

联结多个表

mysql> SELECT prod_name, vend_name, prod_price, quantity FROM orderitems,products,vendors WHERE products.vend_id = vendors.vend_id AND orderitems.prod_id = products.prod_id AND order_num = 20005;
+----------------+-------------+------------+----------+
| prod_name      | vend_name   | prod_price | quantity |
+----------------+-------------+------------+----------+
| .5 ton anvil   | Anvils R Us |       5.99 |       10 |
| 1 ton anvil    | Anvils R Us |       9.99 |        3 |
| TNT (5 sticks) | ACME        |      10.00 |        5 |
| Bird seed      | ACME        |      10.00 |        1 |
+----------------+-------------+------------+----------+
4 rows in set (0.00 sec)

这里的FROM子句列出了3个表,而WHERE子句定义了两个联结条件。

性能考虑:MySQL在运行时关联指定的每个表以联结处理,这种处理可能是非常耗资源的,因此应该仔细,不要联结不必要的表。联结的表越多,性能下降的越厉害。

使用表别名

SELECT cust_name,cust_contact FROM customers AS c, orders AS o,orderitems AS oi WHERE c.cust_id = o.cust_id AND oi.order_num = o.order_num AND prod_id = 'TNT2';

可以看到,FROM子句中3个表全都具有别名,这样做可以缩短SQL语句。
表别名只在查询执行中使用。与列别名不一样,表别名不返回客户机。

使用不同类型的联结

迄今为止,我们使用的只是称为内部联结等值联结的简单联结。还有其它3种联结,它们分别是自联结,自然联结外部联结

自联结

mysql> SELECT prod_id,prod_name FROM products WHERE vend_id = (SELECT vend_id FROM products WHERE prod_id = 'DTNTR');
+---------+----------------+
| prod_id | prod_name      |
+---------+----------------+
| DTNTR   | Detonator      |
| FB      | Bird seed      |
| FC      | Carrots        |
| SAFE    | Safe           |
| SLING   | Sling          |
| TNT1    | TNT (1 stick)  |
| TNT2    | TNT (5 sticks) |
+---------+----------------+
7 rows in set (0.00 sec)

使用自联结后

mysql> 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';
+---------+----------------+
| prod_id | prod_name      |
+---------+----------------+
| DTNTR   | Detonator      |
| FB      | Bird seed      |
| FC      | Carrots        |
| SAFE    | Safe           |
| SLING   | Sling          |
| TNT1    | TNT (1 stick)  |
| TNT2    | TNT (5 sticks) |
+---------+----------------+
7 rows in set (0.01 sec)

查询中需要的两个表实际上是相同的表,因此products表在FROM子句中出现了两次。虽然这是完全合法的,但对products的引用具有二义性,因此MySQL不知道你引用的是products表中的哪个实例。

为解决此问题,使用了表别名。products的第一次出现为别名p1,第二次出现为别名p2。

自然联结

无论何时对表进行联结,应该至少有一个列出现在不止一个表中。标准的联结返回所有数据,甚至相同的列出现多次。自然联结排除多次出现,使每个列只返回一次。
系统不完成这项工作,由自己完成。这一般是通过对表使用通配符(SELECT *),对所有其他表的列使用明确的子集来完成的。

mysql> SELECT c.*, o.order_num, o.order_date,oi.prod_id,oi.quantity FROM customers AS c,orders AS o,orderitems AS oi WHERE c.cust_id = o.cust_id AND oi.order_num = o.order_num AND prod_id = 'FB';
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+
| cust_id | cust_name   | cust_address   | cust_city | cust_state | cust_zip | cust_country | cust_contact | cust_email      | order_num | order_date          | prod_id | quantity |
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+
|   10001 | Coyote Inc. | 200 Maple Lane | Detroit   | MI         | 44444    | USA          | Y Lee        | ylee@coyote.com |     20005 | 2005-09-01 00:00:00 | FB      |        1 |
|   10001 | Coyote Inc. | 200 Maple Lane | Detroit   | MI         | 44444    | USA          | Y Lee        | ylee@coyote.com |     20009 | 2005-10-08 00:00:00 | FB      |        1 |
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+
2 rows in set (0.01 sec)

这个例子中,通配符只对第一个表使用。所有其他列明确列出,所以没有重复的列被检索出来。

外部联结

许多联结表将一个表中的行与另一个表中的行相关联。但有时候会需要包含没有关联行的那些行。

  • 对每个客户下了多少订单进行计数,包括那些尚未下订单的客户
  • 列出所有产品以及订购数量,包括没有人订购的产品
  • 计算平均销售规模,包括那些尚未下订单的客户

下面SELECT语句给出一个简单的内部联结。它检索所有客户及其订单。

mysql> SELECT customers.cust_id, orders.order_num FROM customers INNER JOIN orders ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
5 rows in set (0.00 sec)

外部联结语法类似。为了检索所有客户,包括那些没有订单的客户,可如下进行

mysql> SELECT customers.cust_id, orders.order_num FROM customers LEFT OUTER JOIN orders ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10002 |      NULL |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
6 rows in set (0.00 sec)

与内部联结关联两个表中的行不同的是,外部联结还包括没有关联行的行。在使用OUTER JOIN语法时,必须使用RIGHTLEFT关键字指定包括所有行的表。上面的例子使用LEFT OUTER JOINFROM子句的左边表中选择所有行。

注意:应该总是提供联结条件,否则会得出笛卡儿积。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值