12.1 联结

联结(join),利用SQL的SELECT在数据查询的执行中联结表。

12.1.1 关系表

关系数据库中,关系表的设计是把信息分解成多个表,一类数据一个表,各表通过某些共同的值互相关联。

这样设计的目的是为了使数据库或应用程序具备良好的可伸缩性(scale well)。

可伸缩(scale),能够适应不断增加的工作量而不失败。

12.1.2 为什么使用联结

使用联结(join),可以在一条SELECT语句检索出存储在多个表中的数据。简而言之,联结就是在一条SELECT语句中关联表。

可以联结多个表返回一组输出。

联结不是实体,也就是说在实际的数据库表中并不存在。DBMS根据需要建立联结,它只存在于查询执行期间。

DBMS处理联结可能非常耗费资源,因此不要联结不必要的表。实际上,很多DBMS都对联结表数目有限制。

12.2 创建联结

在一条SELECT语句检索出存储在多个表中的数据,为避免引用的列出现歧义,需要使用完全限定列名。

所有的联结必须使用正确的WHERE子句,否则将返回大量的无效数据。

MariaDB [sqlbzbh]> SELECT * FROM Vendors;
+---------+-----------------+-----------------+------------+------------+----------+--------------+
| vend_id | vend_name | vend_address | vend_city | vend_state | vend_zip | vend_country |
+---------+-----------------+-----------------+------------+------------+----------+--------------+
| BRE02 | Bear Emporium | 500 Park Street | Anytown | OH | 44333 | USA |
| BRS01 | Bears R Us | 123 Main Street | Bear Town | MI | 44444 | USA |
| DLL01 | Doll House Inc. | 555 High Street | Dollsville | CA | 99999 | USA |
| FNG01 | Fun and Games | 42 Galaxy Road | London | NULL | N16 6PS | England |
| FRB01 | Furball Inc. | 1000 5th Avenue | New York | NY | 11111 | USA |
| JTS01 | Jouets et ours | 1 Rue Amusement | Paris | NULL | 45678 | France |
+---------+-----------------+-----------------+------------+------------+----------+--------------+
6 rows in set (0.00 sec) MariaDB [sqlbzbh]>
MariaDB [sqlbzbh]> SELECT * FROM Products;
+---------+---------+---------------------+------------+-----------------------------------------------------------------------+
| prod_id | vend_id | prod_name | prod_price | prod_desc |
+---------+---------+---------------------+------------+-----------------------------------------------------------------------+
| BNBG01 | DLL01 | Fish bean bag toy | 3.49 | Fish bean bag toy, complete with bean bag worms with which to feed it |
| BNBG02 | DLL01 | Bird bean bag toy | 3.49 | Bird bean bag toy, eggs are not included |
| BNBG03 | DLL01 | Rabbit bean bag toy | 3.49 | Rabbit bean bag toy, comes with bean bag carrots |
| BR01 | BRS01 | 8 inch teddy bear | 5.99 | 8 inch teddy bear, comes with cap and jacket |
| BR02 | BRS01 | 12 inch teddy bear | 8.99 | 12 inch teddy bear, comes with cap and jacket |
| BR03 | BRS01 | 18 inch teddy bear | 11.99 | 18 inch teddy bear, comes with cap and jacket |
| RGAN01 | DLL01 | Raggedy Ann | 4.99 | 18 inch Raggedy Ann doll |
| RYL01 | FNG01 | King doll | 9.49 | 12 inch king doll with royal garments and crown |
| RYL02 | FNG01 | Queen doll | 9.49 | 12 inch queen doll with royal garments and crown |
+---------+---------+---------------------+------------+-----------------------------------------------------------------------+
9 rows in set (0.00 sec) MariaDB [sqlbzbh]>
MariaDB [sqlbzbh]> SELECT vend_name, prod_name, prod_price FROM Vendors, Products WHERE Vendors.vend_id = Products.vend_id;
+-----------------+---------------------+------------+
| vend_name | prod_name | prod_price |
+-----------------+---------------------+------------+
| Doll House Inc. | Fish bean bag toy | 3.49 |
| Doll House Inc. | Bird bean bag toy | 3.49 |
| Doll House Inc. | Rabbit bean bag toy | 3.49 |
| Bears R Us | 8 inch teddy bear | 5.99 |
| Bears R Us | 12 inch teddy bear | 8.99 |
| Bears R Us | 18 inch teddy bear | 11.99 |
| Doll House Inc. | Raggedy Ann | 4.99 |
| Fun and Games | King doll | 9.49 |
| Fun and Games | Queen doll | 9.49 |
+-----------------+---------------------+------------+
9 rows in set (0.00 sec) MariaDB [sqlbzbh]>

12.2.1 WHERE子句的重要性

在联结多个表时,实际是将多个表的每一行进行配对,而不管他们逻辑上是否能配在一起。

使用WHERE子句作为过滤条件,可以只匹配符合联结条件的行。

笛卡尔积(cartesian product),由没有联结条件的表关系的结果。这个结果检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。

返回笛卡尔积的联结,也称为叉联结(cross join)。

例如:MariaDB [sqlbzbh]> SELECT vend_name, prod_name, prod_price FROM Vendors, Products;

显然相应的笛卡尔积不是我们想要的。

12.2.2 内联结

联结是基于两个表之间的相等测试,称为等值联结(equipment join)或内联结(inner join)。

以INNER JOIN指定部分FROM子句,联结条件使用特定的ON子句。

ANSI SQL推荐使用INNER JOIN语法。

MariaDB [sqlbzbh]> SELECT vend_name, prod_name, prod_price FROM Vendors INNER JOIN Products ON Vendors.vend_id = Products.vend_id;
+-----------------+---------------------+------------+
| vend_name | prod_name | prod_price |
+-----------------+---------------------+------------+
| Doll House Inc. | Fish bean bag toy | 3.49 |
| Doll House Inc. | Bird bean bag toy | 3.49 |
| Doll House Inc. | Rabbit bean bag toy | 3.49 |
| Bears R Us | 8 inch teddy bear | 5.99 |
| Bears R Us | 12 inch teddy bear | 8.99 |
| Bears R Us | 18 inch teddy bear | 11.99 |
| Doll House Inc. | Raggedy Ann | 4.99 |
| Fun and Games | King doll | 9.49 |
| Fun and Games | Queen doll | 9.49 |
+-----------------+---------------------+------------+
9 rows in set (0.00 sec) MariaDB [sqlbzbh]>

12.2.3 联结多个表

创建多个表的联结时,首先列出所有表,然后定义表之间的关系。

MariaDB [sqlbzbh]> SELECT * FROM OrderItems;
+-----------+------------+---------+----------+------------+
| order_num | order_item | prod_id | quantity | item_price |
+-----------+------------+---------+----------+------------+
| 20005 | 1 | BR01 | 100 | 5.49 |
| 20005 | 2 | BR03 | 100 | 10.99 |
| 20006 | 1 | BR01 | 20 | 5.99 |
| 20006 | 2 | BR02 | 10 | 8.99 |
| 20006 | 3 | BR03 | 10 | 11.99 |
| 20007 | 1 | BR03 | 50 | 11.49 |
| 20007 | 2 | BNBG01 | 100 | 2.99 |
| 20007 | 3 | BNBG02 | 100 | 2.99 |
| 20007 | 4 | BNBG03 | 100 | 2.99 |
| 20007 | 5 | RGAN01 | 50 | 4.49 |
| 20008 | 1 | RGAN01 | 5 | 4.99 |
| 20008 | 2 | BR03 | 5 | 11.99 |
| 20008 | 3 | BNBG01 | 10 | 3.49 |
| 20008 | 4 | BNBG02 | 10 | 3.49 |
| 20008 | 5 | BNBG03 | 10 | 3.49 |
| 20009 | 1 | BNBG01 | 250 | 2.49 |
| 20009 | 2 | BNBG02 | 250 | 2.49 |
| 20009 | 3 | BNBG03 | 250 | 2.49 |
+-----------+------------+---------+----------+------------+
18 rows in set (0.00 sec) MariaDB [sqlbzbh]>
MariaDB [sqlbzbh]> 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 = 20007;
+---------------------+-----------------+------------+----------+
| prod_name | vend_name | prod_price | quantity |
+---------------------+-----------------+------------+----------+
| 18 inch teddy bear | Bears R Us | 11.99 | 50 |
| Fish bean bag toy | Doll House Inc. | 3.49 | 100 |
| Bird bean bag toy | Doll House Inc. | 3.49 | 100 |
| Rabbit bean bag toy | Doll House Inc. | 3.49 | 100 |
| Raggedy Ann | Doll House Inc. | 4.99 | 50 |
+---------------------+-----------------+------------+----------+
5 rows in set (0.00 sec) MariaDB [sqlbzbh]>

改写“SQL必知必会11--使用子查询”中的示例

MariaDB [sqlbzbh]> SELECT cust_name,cust_contact FROM Customers WHERE cust_id IN (SELECT cust_id FROM Orders WHERE order_num IN (SELECT order_num FROM OrderItems WHERE prod_id = 'RGAN01'));
+---------------+--------------------+
| cust_name | cust_contact |
+---------------+--------------------+
| Fun4All | Denise L. Stephens |
| The Toy Store | Kim Howard |
+---------------+--------------------+
2 rows in set (0.00 sec) MariaDB [sqlbzbh]>
MariaDB [sqlbzbh]> SELECT cust_name,cust_contact
-> FROM Customers, Orders, OrderItems
-> WHERE Customers.cust_id = Orders.cust_id
-> AND OrderItems.order_num = Orders.order_num
-> AND prod_id = 'RGAN01';
+---------------+--------------------+
| cust_name | cust_contact |
+---------------+--------------------+
| Fun4All | Denise L. Stephens |
| The Toy Store | Kim Howard |
+---------------+--------------------+
2 rows in set (0.00 sec) MariaDB [sqlbzbh]>