15.1.1 联结案例
现在,假如有由同一供应商生产的多种物品,那么在何处存储供应商信息(如,供应商名、地址、联系方法等)呢?
将产品信息和供应商信息分开。
可建立两个表,一个存储供应商信息,另一个存储 产品信息。vendors表包含所有供应商信息,每个供应商占一行,每个供 应商具有唯一的标识。此标识称为主键(primary key)。
products表只存储产品信息,它除了存储供应商ID(vendors表的主 键)外不存储其他供应商信息。vendors表的主键又叫作products的外键, 它将vendors表与products表关联,利用供应商ID能从vendors表中找出 相应供应商的详细信息。
外键(foreignkey) 外键为某个表中的一列,它包含另一个表 的主键值,定义了两个表之间的关系。
可伸缩性(scale) 能够适应不断增加的工作量而不失败。设 计良好的数据库或应用程序称之为可伸缩性好(scale well)
15.1.2 为什么要使用联结
联结允许使用单条SELECT语句检索出数据。
维护引用完整性 如果在products表中插入拥有非法供应商ID (即没有在vendors表中出现)的供应商生产的产品,则这些产品是不可访问的,因为它们没有关联到某个供应商。为防止这种情况发生,可指示MySQL只允许在products表的供应商ID列中出现合法值(即出现在vendors表中的供应商)。 这就是维护引用完整性,它是通过在表的定义中指定主键和外键来实现的。
15.2 创建联结
联结的创建非常简单,规定要联结的所有表以及它们如何关联即可。
SELECT vend_name, prod_name, prod_price FROM vendors, products WHERE vendors.vend_id = products.vend_id ORDER BY vend_name, prod_name;
完全限定列名 在引用的列可能出现二义性时,必须使用完全限定列名(用一个点分隔的表名和列名)。如果引用一个 没有用表名限制的具有二义性的列名,MySQL将返回错误。
15.2.1 WHERE子句对创建联结非常重要,WHERE子句可以作为过滤条件,只包含那些匹配给定条件的行,没有WHERE子句,第一个表中的每个行将与第二个表中的每个行配对,而不管它们逻辑上是否可以配在一起。
笛卡儿积(cartesian product) 由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
不要忘了WHERE子句 应该保证所有联结都有WHERE子句,否则MySQL将返回比想要的数据多得多的数据。同理,应该保证WHERE子句的正确性。不正确的过滤条件将导致MySQL返回不正确的数据。
叉联结 有时我们会听到返回称为叉联结(crossjoin)的笛卡儿积的联结类型。
15.2.2 内部联结
目前为止所用的联结称为等值联结(equijoin),它基于两个表之间的相等测试。这种联结也称为内部联结。
上面的例子可用下面的的语句完成:
SELECT vend_name, prod_name, prod_price FROM vendors INNER JOIN products ON vendors.vend_id == products.vend_id
使用哪种语法 ANSI SQL规范首选INNER JOIN语法。此外, 尽管使用WHERE子句定义联结的确比较简单,但是使用明确的联结语法能够确保不会忘记联结条件,有时候这样做也能影响性能。
15.2.3 联结多个表
SQL对一条SELECT语句中可以联结的表的数目没有限制。创建联结的基本规则也相同。首先列出所有表,然后定义表之间的关系。
此例子显示编号为20005的订单中的物品。订单物品存储在orderitems表中。每个产品按其产品ID存储,它引用products表中的产品。这些产品通过供应商ID联结到vendors表中相应的供应商, 供应商ID存储在每个产品的记录中。这里的FROM子句列出了3个表,而 WHERE子句定义了这两个联结条件,而第三个联结条件用来过滤出订单20005中的物品。
性能考虑 MySQL在运行时关联指定的每个表以处理联结。 这种处理可能是非常耗费资源的,因此应该仔细,不要联结 不必要的表。联结的表越多,性能下降越厉害。
子查询可以用联结查询代替,14章例子中的嵌套子查询可以用两个联结代替。