day12-联结表

day12-联结表

联结

SQL 最强大的功能之一就是能在数据查询的执行中联结(join)表。联结是利用 SQL 的 SELECT 能执行的最重要的操作,很好地理解联结及其语法是学习 SQL 的极为重要的部分。 
在能够有效地使用联结前,必须了解关系表以及关系数据库设计的一些基础知识。下面的介绍并不能涵盖这一主题的所有内容,但作为入门已经够了。

关系表

理解关系表,最好是来看个例子。
有一个包含产品目录的数据库表,其中每类物品占一行。对于每一种物品,要存储的信息包括产品描述、价格,以及生产该产品的供应商。

现在有同一供应商生产的多种物品,那么在何处存储供应商名、地址、联系方法等供应商信息呢?将这些数据与产品信息分开存储的理由是: 
 同一供应商生产的每个产品,其供应商信息都是相同的,对每个产品重复此信息既浪费时间又浪费存储空间;
 如果供应商信息发生变化,例如供应商迁址或电话号码变动,只需修改一次即可;
 如果有重复数据(即每种产品都存储供应商信息),则很难保证每次输入该数据的方式都相同。不一致的数据在报表中就很难利用。

关键是,相同的数据出现多次决不是一件好事,这是关系数据库设计的基础。关系表的设计就是要把信息分解成多个表,一类数据一个表。各表通过某些共同的值互相关联(所以才叫关系数据库)。

在这个例子中可建立两个表:一个存储供应商信息,另一个存储产品信息。Vendors 表包含所有供应商信息,每个供应商占一行,具有唯一的标识。此标识称为主键(primary key),可以是供应商 ID 或任何其他唯一值。

Products 表只存储产品信息,除了存储供应商 ID(Vendors 表的主键)外,它不存储其他有关供应商的信息。Vendors 表的主键将 Vendors 表与 Products 表关联,利用供应商 ID 能从 Vendors 表中找出相应供应商的详细信息。
这样做的好处是: 
 供应商信息不重复,不会浪费时间和空间;
 如果供应商信息变动,可以只更新 Vendors 表中的单个记录,相关表中的数据不用改动;
 由于数据不重复,数据显然是一致的,使得处理数据和生成报表更简单。

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


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

为什么使用联结

如前所述,将数据分解为多个表能更有效地存储,更方便地处理,并且可伸缩性更好。但这些好处是有代价的。

如果数据存储在多个表中,怎样用一条 SELECT 语句就检索出数据呢?
答案是使用联结。简单说,联结是一种机制,用来在一条 SELECT 语句中关联表,因此称为联结。使用特殊的语法,可以联结多个表返回一组输出,联结在运行时关联表中正确的行。



说明:使用交互式 DBMS 工具 
重要的是,要理解联结不是物理实体。换句话说,它在实际的数据库表中并不存在。DBMS 会根据需要建立联结,它在查询执行期间一直存在。
许多 DBMS 提供图形界面,用来交互式地定义表关系。这些工具极其有助于维护引用完整性。在使用关系表时,仅在关系列中插入合法数据是非常重要的。回到这里的例子,如果 Products 表中存储了无效的供应商 ID,则相应的产品不可访问,因为它们没有关联到某个供应商。为避免这种情况发生,可指示数据库只允许在 Products 表的供应商 ID 列中出现合法值(即出现在 Vendors 表中的供应商)。引用完整性表示 DBMS 强制实施数据完整性规则。这些规则一般由提供了界面的 DBMS 管理。

创建联结

SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;



输出:
mysql> SELECT vend_name, prod_name, prod_price
    -> FROM Vendors, Products
    -> WHERE Vendors.vend_id = Products.vend_id;
+-----------------+---------------------+------------+
| vend_name       | prod_name           | prod_price |
+-----------------+---------------------+------------+
| 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. | Fish bean bag toy   |       3.49 |
| Doll House Inc. | Bird bean bag toy   |       3.49 |
| Doll House Inc. | Rabbit bean bag toy |       3.49 |
| 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.01 sec)

mysql> 





我们来看这段代码。SELECT 语句与前面所有语句一样指定要检索的列。
这里最大的差别是所指定的两列(prod_name 和 prod_price)在一个表中,而第一列(vend_name)在另一个表中。

现在来看 FROM 子句。与以前的 SELECT 语句不一样,这条语句的 FROM子句列出了两个表:Vendors 和 Products。它们就是这条 SELECT 语句联结的两个表的名字。这两个表用 WHERE 子句正确地联结,WHERE 子句指示 DBMS 将 Vendors 表中的 vend_id 与 Products 表中的 vend_id 匹配起来。

可以看到,要匹配的两列指定为 Vendors.vend_id 和 Products.vend_id。这里需要这种完全限定列名,如果只给出 vend_id,DBMS 就不知道指的是哪一个(每个表中有一个)。从前面的输出可以看到,一条 SELECT 语句返回了两个不同表中的数据。

警告:完全限定列名 
就像前一课提到的,在引用的列可能出现歧义时,必须使用完全限定列名(用一个句点分隔表名和列名)。如果引用一个没有用表名限制的具有歧义的列名,大多数 DBMS 会返回错误。

where子句的重要性

使用 WHERE 子句建立联结关系似乎有点奇怪,但实际上是有个很充分的理由的。要记住,在一条 SELECT 语句中联结几个表时,相应的关系是在运行中构造的。在数据库表的定义中没有指示 DBMS 如何对表进行联结的内容。你必须自己做这件事情。在联结两个表时,实际要做的是将
第一个表中的每一行与第二个表中的每一行配对。WHERE 子句作为过滤条件,只包含那些匹配给定条件(这里是联结条件)的行。没有 WHERE子句,第一个表中的每一行将与第二个表中的每一行配对,而不管它们逻辑上是否能配在一起。

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

理解这一点,请看下面的 SELECT 语句及其输出:



SELECT vend_name, prod_name, prod_price
FROM Vendors, Products;



输出:

mysql> SELECT vend_name, prod_name, prod_price
    -> FROM Vendors, Products;
+-----------------+---------------------+------------+
| vend_name       | prod_name           | prod_price |
+-----------------+---------------------+------------+
| Jouets et ours  | Fish bean bag toy   |       3.49 |
| Furball Inc.    | Fish bean bag toy   |       3.49 |
| Fun and Games   | Fish bean bag toy   |       3.49 |
| Doll House Inc. | Fish bean bag toy   |       3.49 |
| Bears R Us      | Fish bean bag toy   |       3.49 |
| Bear Emporium   | Fish bean bag toy   |       3.49 |
| Jouets et ours  | Bird bean bag toy   |       3.49 |
| Furball Inc.    | Bird bean bag toy   |       3.49 |
| Fun and Games   | Bird bean bag toy   |       3.49 |
| Doll House Inc. | Bird bean bag toy   |       3.49 |
| Bears R Us      | Bird bean bag toy   |       3.49 |
| Bear Emporium   | Bird bean bag toy   |       3.49 |
| Jouets et ours  | Rabbit bean bag toy |       3.49 |
| Furball Inc.    | Rabbit bean bag toy |       3.49 |
| Fun and Games   | Rabbit bean bag toy |       3.49 |
| Doll House Inc. | Rabbit bean bag toy |       3.49 |
| Bears R Us      | Rabbit bean bag toy |       3.49 |
| Bear Emporium   | Rabbit bean bag toy |       3.49 |
| Jouets et ours  | 8 inch teddy bear   |       5.99 |
| Furball Inc.    | 8 inch teddy bear   |       5.99 |
| Fun and Games   | 8 inch teddy bear   |       5.99 |
| Doll House Inc. | 8 inch teddy bear   |       5.99 |
| Bears R Us      | 8 inch teddy bear   |       5.99 |
| Bear Emporium   | 8 inch teddy bear   |       5.99 |
| Jouets et ours  | 12 inch teddy bear  |       8.99 |
| Furball Inc.    | 12 inch teddy bear  |       8.99 |
| Fun and Games   | 12 inch teddy bear  |       8.99 |
| Doll House Inc. | 12 inch teddy bear  |       8.99 |
| Bears R Us      | 12 inch teddy bear  |       8.99 |
| Bear Emporium   | 12 inch teddy bear  |       8.99 |
| Jouets et ours  | 18 inch teddy bear  |      11.99 |
| Furball Inc.    | 18 inch teddy bear  |      11.99 |
| Fun and Games   | 18 inch teddy bear  |      11.99 |
| Doll House Inc. | 18 inch teddy bear  |      11.99 |
| Bears R Us      | 18 inch teddy bear  |      11.99 |
| Bear Emporium   | 18 inch teddy bear  |      11.99 |
| Jouets et ours  | Raggedy Ann         |       4.99 |
| Furball Inc.    | Raggedy Ann         |       4.99 |
| Fun and Games   | Raggedy Ann         |       4.99 |
| Doll House Inc. | Raggedy Ann         |       4.99 |
| Bears R Us      | Raggedy Ann         |       4.99 |
| Bear Emporium   | Raggedy Ann         |       4.99 |
| Jouets et ours  | King doll           |       9.49 |
| Furball Inc.    | King doll           |       9.49 |
| Fun and Games   | King doll           |       9.49 |
| Doll House Inc. | King doll           |       9.49 |
| Bears R Us      | King doll           |       9.49 |
| Bear Emporium   | King doll           |       9.49 |
| Jouets et ours  | Queen doll          |       9.49 |
| Furball Inc.    | Queen doll          |       9.49 |
| Fun and Games   | Queen doll          |       9.49 |
| Doll House Inc. | Queen doll          |       9.49 |
| Bears R Us      | Queen doll          |       9.49 |
| Bear Emporium   | Queen doll          |       9.49 |
+-----------------+---------------------+------------+
54 rows in set (0.00 sec)

mysql> 





从上面的输出可以看到,相应的笛卡儿积不是我们想要的。这里返回的数据用每个供应商匹配了每个产品,包括了供应商不正确的产品(即使供应商根本就没有产品)。

注意:不要忘了 WHERE 子句 
要保证所有联结都有 WHERE 子句,否则 DBMS 将返回比想要的数据多得多的数据。同理,要保证 WHERE 子句的正确性。不正确的过滤条件会导致 DBMS 返回不正确的数据。

提示:叉联结 
有时,返回笛卡儿积的联结,也称叉联结(cross join)。

内联结

目前为止使用的联结称为等值联结(equijoin),它基于两个表之间的相等测试。这种联结也称为内联结(inner join)。其实,可以对这种联结使用稍微不同的语法,明确指定联结的类型。下面的 SELECT 语句返回与前面例子完全相同的数据:





SELECT vend_name, prod_name, prod_price
FROM Vendors
INNER JOIN Products ON Vendors.vend_id = Products.vend_id;


输出:
mysql> 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)

mysql> 





此语句中的 SELECT 与前面的 SELECT 语句相同,但 FROM 子句不同。这里,两个表之间的关系是以 INNER JOIN 指定的部分 FROM 子句。在使用这种语法时,联结条件用特定的 ON 子句而不是 WHERE 子句给出。传递给 ON 的实际条件与传递给 WHERE 的相同。
至于选用哪种语法,请参阅具体的 DBMS 文档。

说明:“正确的”语法 
ANSI SQL 规范首选 INNER JOIN 语法,之前使用的是简单的等值语法。其实,SQL 语言纯正论者是用鄙视的眼光看待简单语法的。这就是说,DBMS 的确支持简单格式和标准格式,我建议你要理解这两种格式,具体使用就看你用哪个更顺手了。

联结多个表

SQL 不限制一条 SELECT 语句中可以联结的表的数目。创建联结的基本
规则也相同。首先列出所有表,然后定义表之间的关系。例如:



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;



输出:
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 = 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)

mysql> 






这个例子显示订单 20007 中的物品。订单物品存储在 OrderItems 表中。每个产品按其产品 ID 存储,它引用 Products 表中的产品。这些产品通过供应商 ID 联结到 Vendors 表中相应的供应商,供应商 ID 存储在每个产品的记录中。这里的 FROM 子句列出三个表,WHERE 子句定义这两个联结条件,而第三个联结条件用来过滤出订单 20007 中的物品。

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

注意:联结中表的最大数目 
虽然 SQL 本身不限制每个联结约束中表的数目,但实际上许多 DBMS都有限制。请参阅具体的 DBMS 文档以了解其限制。





现在回顾一下第 11课中的例子,如下的 SELECT 语句返回订购产品 RGAN01的顾客列表:

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'));
 
 
 输出:
 mysql> 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)

mysql> 



 
 
 
 
 
 子查询并不总是执行复杂 SELECT 操作的最有效方法,下面是使用联结的相同查询:


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';




输出:
mysql> 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)

mysql> 







这个查询中的返回数据需要使用 3个表。但在这里,我们没有在嵌套子查询中使用它们,而是使用了两个联结来连接表。这里有三个 WHERE 子句条件。前两个关联联结中的表,后一个过滤产品 RGAN01 的数据。




提示:多做实验 
可以看到,执行任一给定的 SQL 操作一般不止一种方法。很少有绝对正确或绝对错误的方法。性能可能会受操作类型、所使用的 DBMS、表中数据量、是否存在索引或键等条件的影响。因此,有必要试验不同的选择机制,找出最适合具体情况的

方法。
说明:联结的列名 
上述所有例子里,联结的几个列的名字都是一样的(例如 Customers和 Orders 表里的列都叫 cust_id)。列名相同并不是必需的,而且你经常会遇到命名规范不同的数据库。我这样建表只是为了简单起见。

小结

联结是 SQL 中一个最重要、最强大的特性,有效地使用联结需要对关系数据库设计有基本的了解。本课在介绍联结时,讲述了一些关系数据库设计的基本知识,包括等值联结(也称为内联结)这种最常用的联结。

下一课将介绍如何创建其他类型的联结。

小练习

1.编写 SQL 语句,返回 Customers 表中的顾客名称(cust_name)和Orders 表中的相关订单号(order_num),并 按顾客名称再按订单号对结果进行排序。实际上是尝试两次,一次使用简单的等联结语法, 一次使用 INNER JOIN。

-- Equijoin syntax
SELECT cust_name, order_num
FROM Customers, Orders
WHERE Customers.cust_id = Orders.cust_id
ORDER BY cust_name, order_num;



输出:

mysql> SELECT cust_name, order_num
    -> FROM Customers, Orders
    -> WHERE Customers.cust_id = Orders.cust_id
    -> ORDER BY cust_name, order_num;
+---------------+-----------+
| cust_name     | order_num |
+---------------+-----------+
| Fun4All       |     20006 |
| Fun4All       |     20007 |
| The Toy Store |     20008 |
| Village Toys  |     20005 |
| Village Toys  |     20009 |
+---------------+-----------+
5 rows in set (0.00 sec)

mysql> 




-- ANSI INNER JOIN syntax
SELECT cust_name, order_num
FROM Customers INNER JOIN Orders
 ON Customers.cust_id = Orders.cust_id
ORDER BY cust_name, order_num;





输出:
mysql> SELECT cust_name, order_num
    -> FROM Customers INNER JOIN Orders
    ->  ON Customers.cust_id = Orders.cust_id
    -> ORDER BY cust_name, order_num;
+---------------+-----------+
| cust_name     | order_num |
+---------------+-----------+
| Fun4All       |     20006 |
| Fun4All       |     20007 |
| The Toy Store |     20008 |
| Village Toys  |     20005 |
| Village Toys  |     20009 |
+---------------+-----------+
5 rows in set (0.00 sec)

mysql>

2.我们来让上一题变得更有用些。除了返回顾客名称和订单号,添加第三列 OrderTotal,其中包含每个订单的总价。有两种方法可以执行此操作:使用 OrderItems 表的子查询来创建 OrderTotal 列,或者将 OrderItems 表与现有表联结并使用聚合函数。提示:请注意需要使用完全限定列名的地方。

-- Solution using subqueries
SELECT cust_name,
       order_num,
       (SELECT Sum(item_price*quantity)
       FROM OrderItems
       WHERE Orders.order_num=OrderItems.order_num) AS OrderTotal
FROM Customers, Orders
WHERE Customers.cust_id = Orders.cust_id
ORDER BY cust_name, order_num;




输出:

mysql> SELECT cust_name,
    ->        order_num,
    ->        (SELECT Sum(item_price*quantity)
    ->        FROM OrderItems
    ->        WHERE Orders.order_num=OrderItems.order_num) AS OrderTotal
    -> FROM Customers, Orders
    -> WHERE Customers.cust_id = Orders.cust_id
    -> ORDER BY cust_name, order_num;
+---------------+-----------+------------+
| cust_name     | order_num | OrderTotal |
+---------------+-----------+------------+
| Fun4All       |     20006 |     329.60 |
| Fun4All       |     20007 |    1696.00 |
| The Toy Store |     20008 |     189.60 |
| Village Toys  |     20005 |    1648.00 |
| Village Toys  |     20009 |    1867.50 |
+---------------+-----------+------------+
5 rows in set (0.00 sec)






-- Solution using joins
SELECT cust_name,
       Orders.order_num,
       Sum(item_price*quantity) AS OrderTotal
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
 AND Orders.order_num = OrderItems.order_num
GROUP BY cust_name, Orders.order_num
ORDER BY cust_name, order_num;




输出:
mysql> SELECT cust_name,
    ->        Orders.order_num,
    ->        Sum(item_price*quantity) AS OrderTotal
    -> FROM Customers, Orders, OrderItems
    -> WHERE Customers.cust_id = Orders.cust_id
    ->  AND Orders.order_num = OrderItems.order_num
    -> GROUP BY cust_name, Orders.order_num
    -> ORDER BY cust_name, order_num;
+---------------+-----------+------------+
| cust_name     | order_num | OrderTotal |
+---------------+-----------+------------+
| Fun4All       |     20006 |     329.60 |
| Fun4All       |     20007 |    1696.00 |
| The Toy Store |     20008 |     189.60 |
| Village Toys  |     20005 |    1648.00 |
| Village Toys  |     20009 |    1867.50 |
+---------------+-----------+------------+
5 rows in set (0.00 sec)

mysql>

3.我们重新看一下第 11 课的挑战题 2。编写 SQL 语句,检索订购产品BR01 的日期,这一次使用联结和简单的等联结语法。输出应该与第11 课的输出相同。

SELECT cust_id, order_date
FROM Orders, OrderItems
WHERE Orders.order_num = OrderItems.order_num
 AND prod_id = 'BR01'
ORDER BY order_date;


输出:


mysql> SELECT cust_id, order_date
    -> FROM Orders, OrderItems
    -> WHERE Orders.order_num = OrderItems.order_num
    ->  AND prod_id = 'BR01'
    -> ORDER BY order_date;
+------------+---------------------+
| cust_id    | order_date          |
+------------+---------------------+
| 1000000003 | 2020-01-12 00:00:00 |
| 1000000001 | 2020-05-01 00:00:00 |
+------------+---------------------+
2 rows in set (0.00 sec)

mysql>

4.很有趣,我们再试一次。重新创建为第 11 课挑战题 3 编写的 SQL 语句,这次使用 ANSI 的 INNER JOIN 语法。在之前编写的代码中使用了两个嵌套的子查询。要重新创建它,需要两个 INNER JOIN 语句,每个语句的格式类似于本课讲到的 INNER JOIN 示例,而且不要忘记WHERE 子句可以通过 prod_id 进行过滤。

SELECT cust_email
FROM Customers
 INNER JOIN Orders ON Customers.cust_id = Orders.cust_id
 INNER JOIN OrderItems ON Orders.order_num = OrderItems.order_num
WHERE prod_id = 'BR01';


输出:


mysql> SELECT cust_email
    -> FROM Customers
    ->  INNER JOIN Orders ON Customers.cust_id = Orders.cust_id
    ->  INNER JOIN OrderItems ON Orders.order_num = OrderItems.order_num
    -> WHERE prod_id = 'BR01';
+-----------------------+
| cust_email            |
+-----------------------+
| sales@villagetoys.com |
| jjones@fun4all.com    |
+-----------------------+
2 rows in set (0.00 sec)

mysql>

5.再让事情变得更加有趣些,我们将混合使用联结、聚合函数和分组。 准备好了吗?回到第 10 课,当时的挑战是要求查找值等于或大于 1000 的所有订单号。这些结果很有用,但更有用的是订单数量至少达到 这个数的顾客名称。因此,编写 SQL 语句,使用联结从 Customers 表返回顾客名称(cust_name),并从 OrderItems 表返回所有订单的总价。

提示:要联结这些表,还需要包括 Orders 表(因为 Customers 表 与 OrderItems 表不直接相关,Customers 表与 Orders 表相关,而 Orders 表与 OrderItems 表相关)。不要忘记 GROUP BY 和 HAVING,并按顾客名称对结果进行排序。你可以使用简单的等联结或 ANSI 的INNER JOIN 语法。或者,如果你很勇敢,请尝试使用两种方式编写。
-- Equijoin syntax
SELECT cust_name, SUM(item_price*quantity) AS total_price
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
 AND Orders.order_num = OrderItems.order_num
GROUP BY cust_name HAVING SUM(item_price*quantity) >= 1000
ORDER BY cust_name;


输出:
mysql> SELECT cust_name, SUM(item_price*quantity) AS total_price
    -> FROM Customers, Orders, OrderItems
    -> WHERE Customers.cust_id = Orders.cust_id
    ->  AND Orders.order_num = OrderItems.order_num
    -> GROUP BY cust_name HAVING SUM(item_price*quantity) >= 1000
    -> ORDER BY cust_name;
+--------------+-------------+
| cust_name    | total_price |
+--------------+-------------+
| Fun4All      |     2025.60 |
| Village Toys |     3515.50 |
+--------------+-------------+
2 rows in set (0.00 sec)



-- ANSI INNER JOIN syntax
SELECT cust_name, SUM(item_price*quantity) AS total_price
FROM Customers
 INNER JOIN Orders ON Customers.cust_id = Orders.cust_id
 INNER JOIN OrderItems ON Orders.order_num = OrderItems.order_num
GROUP BY cust_name
HAVING SUM(item_price*quantity) >= 1000
ORDER BY cust_name;



输出:
mysql> SELECT cust_name, SUM(item_price*quantity) AS total_price
    -> FROM Customers
    ->  INNER JOIN Orders ON Customers.cust_id = Orders.cust_id
    ->  INNER JOIN OrderItems ON Orders.order_num = OrderItems.order_num
    -> GROUP BY cust_name
    -> HAVING SUM(item_price*quantity) >= 1000
    -> ORDER BY cust_name;
+--------------+-------------+
| cust_name    | total_price |
+--------------+-------------+
| Fun4All      |     2025.60 |
| Village Toys |     3515.50 |
+--------------+-------------+
2 rows in set (0.00 sec)

mysql>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值