MySQL基础之联结表
联结
SQL最强大的功能之一就是能在数据检索查询的执行中联结(join)表。联结是利用SQL的SELECT能执行的最重要的操作。
关系表
关系表的设计就是要保证把信息分解成多个表, 一类数据一个表。各表通过某些常用的值(即关系设计中的关系(relational))互相关联。
关系数据可以有效地存储和方便的处理。因此, 关系数据库的可伸缩性远比非关系数据库要好。
可伸缩性(scale):
能够适应于不断增加的工作量而不失败。设计良好的数据库或应用程序称之为可伸缩性好
为什么要使用联结
联结是一种机制, 用来在一条SELECT语句中关联表, 因此称之为联结。使用特殊的语法, 可以联结多个表返回一组输出, 联结在运行时关联表中正确的行。
维护引用完整性
联结不是物理实体, 换句话说, 它在实际的数据库表中不存在。联结由MySQL根据需要建立, 它存在于查询的执行命令中
创建联结
联结的创建很简单, 规定要联结的所有表以及他们如何关联即可。
MariaDB [crashcourse]> 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.007 sec) MariaDB [crashcourse]>
SELECT语句与前面所有语句一样指定要检索的列,最大的差别是所指定的两个列(prod_name和prod_price)在一个表中,而另一个列(vend_name)在另一个表中;
FROM子句列出了两个表,就是这SELECT语句联结的两个表的名字,用WHERE子句正确联结:指示MySQL 匹配vendors表中的vend_id和products表中的vend_id, 这里需要使用完全限定列名, 因为如果只给出vend_id, 则MySQL不知道指的是哪一个(他们有两个, 每个表中有一个)
WHERE子句的重要性
在一条SELECT语句中联结几个表时, 相应的关系是在运行中构造的。在数据库定义中不存在能指示MySQL如何对表进行联结的东西。必须自己做这件事情。在联结两个表时, 实际上做的是将第一个表中的每一行与第二个表中的每一行进行配对。WHERE子句作为过滤条件。它只包含那些匹配给定条件(联结条件)的行。没有WHERE子句, 第一个表中的每个行将与第二个表中的每个行匹配。而不管它们逻辑上是否可以配在一起。
笛卡尔积
由没有联结条件的表关系返回的结果为笛卡尔积。检索出的行的数目将是第一个表中行数乘以第二个表中的行数
MariaDB [crashcourse]> SELECT vend_name, prod_name, prod_price FROM vendors, products ORDER BY vend_name, prod_name;+----------------+----------------+------------+ | vend_name | prod_name | prod_price | +----------------+----------------+------------+ | ACME | .5 ton anvil | 5.99 | | ACME | 1 ton anvil | 9.99 | | ACME | 2 ton anvil | 14.99 | | ACME | Bird seed | 10.00 | | ACME | Carrots | 2.50 | | ACME | Detonator | 13.00 | | ACME | Fuses | 3.42 | | ACME | JetPack 1000 | 35.00 | | ACME | JetPack 2000 | 55.00 | | ACME | Oil can | 8.99 | | 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 | | Anvils R Us | Bird seed | 10.00 | | Anvils R Us | Carrots | 2.50 | | Anvils R Us | Detonator | 13.00 | | Anvils R Us | Fuses | 3.42 | | Anvils R Us | JetPack 1000 | 35.00 | | Anvils R Us | JetPack 2000 | 55.00 | | Anvils R Us | Oil can | 8.99 | | Anvils R Us | Safe | 50.00 | | Anvils R Us | Sling | 4.49 | | Anvils R Us | TNT (1 stick) | 2.50 | | Anvils R Us | TNT (5 sticks) | 10.00 | | Furball Inc. | .5 ton anvil | 5.99 | | Furball Inc. | 1 ton anvil | 9.99 | | Furball Inc. | 2 ton anvil | 14.99 | | Furball Inc. | Bird seed | 10.00 | | Furball Inc. | Carrots | 2.50 | | Furball Inc. | Detonator | 13.00 | | Furball Inc. | Fuses | 3.42 | | Furball Inc. | JetPack 1000 | 35.00 | | Furball Inc. | JetPack 2000 | 55.00 | | Furball Inc. | Oil can | 8.99 | | Furball Inc. | Safe | 50.00 | | Furball Inc. | Sling | 4.49 | | Furball Inc. | TNT (1 stick) | 2.50 | | Furball Inc. | TNT (5 sticks) | 10.00 | | Jet Set | .5 ton anvil | 5.99 | | Jet Set | 1 ton anvil | 9.99 | | Jet Set | 2 ton anvil | 14.99 | | Jet Set | Bird seed | 10.00 | | Jet Set | Carrots | 2.50 | | Jet Set | Detonator | 13.00 | | Jet Set | Fuses | 3.42 | | Jet Set | JetPack 1000 | 35.00 | | Jet Set | JetPack 2000 | 55.00 | | Jet Set | Oil can | 8.99 | | Jet Set | Safe | 50.00 | | Jet Set | Sling | 4.49 | | Jet Set | TNT (1 stick) | 2.50 | | Jet Set | TNT (5 sticks) | 10.00 | | Jouets Et Ours | .5 ton anvil | 5.99 | | Jouets Et Ours | 1 ton anvil | 9.99 | | Jouets Et Ours | 2 ton anvil | 14.99 | | Jouets Et Ours | Bird seed | 10.00 | | Jouets Et Ours | Carrots | 2.50 | | Jouets Et Ours | Detonator | 13.00 | | Jouets Et Ours | Fuses | 3.42 | | Jouets Et Ours | JetPack 1000 | 35.00 | | Jouets Et Ours | JetPack 2000 | 55.00 | | Jouets Et Ours | Oil can | 8.99 | | Jouets Et Ours | Safe | 50.00 | | Jouets Et Ours | Sling | 4.49 | | Jouets Et Ours | TNT (1 stick) | 2.50 | | Jouets Et Ours | TNT (5 sticks) | 10.00 | | LT Supplies | .5 ton anvil | 5.99 | | LT Supplies | 1 ton anvil | 9.99 | | LT Supplies | 2 ton anvil | 14.99 | | LT Supplies | Bird seed | 10.00 | | LT Supplies | Carrots | 2.50 | | LT Supplies | Detonator | 13.00 | | LT Supplies | Fuses | 3.42 | | LT Supplies | JetPack 1000 | 35.00 | | LT Supplies | JetPack 2000 | 55.00 | | LT Supplies | Oil can | 8.99 | | LT Supplies | Safe | 50.00 | | LT Supplies | Sling | 4.49 | | LT Supplies | TNT (1 stick) | 2.50 | | LT Supplies | TNT (5 sticks) | 10.00 | +----------------+----------------+------------+ 84 rows in set (0.003 sec) MariaDB [crashcourse]>
应该保证所有联结都有WHERE子句,否则MySQL将返回比想要的数据多得多的数据;
内部联结
目前所用到的联结都称为等值联结, 它是基于两个表之间的相等测试。这种联结也称为内部联结。其实, 对于这种联结可以使用稍微不同的语法来明确指定联结的类型。
MariaDB [crashcourse]> SELECT vend_name, prod_name, prod_price FROM vendors INNER JOIN products ON 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.001 sec) MariaDB [crashcourse]>
联结多个表
SQL对一条SELECT语句中可以联结的表的数目没有限制。创建联结的基本规则也相同
MariaDB [crashcourse]> 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.001 sec) MariaDB [crashcourse]>
性能考虑
MySQL在运行时关联指定的每个表以处理联结。这种处理可能是非常消耗资源的。