最近遇到了点瓶颈,主要是Mysql的外键着实不熟悉,之前即使是几百万的数据也都是存放在一张表中,昨儿又开始拿起了《MySQL必知必会》,开始恶补一些Mysql基础知识,Mysql的联结表,主要是《MySQL必知必会》第15章和第16章的内容,相关建表+数据sql语句,可在下方参阅链接中下载。关于操作Mysql数据库的工具,有 Mysql workbench,Navicat等,推荐使用Navicat for Mysql(免费)。
假如有一个包含产品目录的数据库表,其中每种类别的物品占一行。对于每种物品要存储的信息包括产品描述和价格,以及生产该产品的供应商信息。现在,假如有由同一供应商生产的多种物品,那么在何处存储供应商信息(如,供应商名、地址、联系方法等)呢?将这些数据与产品信息分开存储的理由如下
- 同一供应商生产的每个产品的供应商信息都是相同的,对每个产品重复此信息既浪费时间又浪费存储空间
- 如果供应商信息改变(例如,供应商搬家或电话号码变动),只需改动一次即可
- 如果有重复数据(即每种产品都存储供应商信息),很难保证每次输入该数据的方式都相同
关系表的设计就是要保证把信息分解成多个表,一类数据一个表。各表通过某些常用的值(即关系设计中的关系(relational))互相关联。
在这个例子中,可建立两个表,一个存储供应商信息,另一个存储产品信息。vendors表包含所有供应商信息,每个供应商占一行,每个供应商具有唯一的标识。products表只存储产品信息,它除了存储供应商ID(vendors表的主键)外不存储其他供应商信息。vendors表的主键又叫作products的外键(foreign key,外键为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系),它将vendors表与products表关联,利用供应商ID能从vendors表中找出相应供应商的详细信息。
如果数据存储在多个表中,想通过单条SELECT语句检索出数据必须使用联结。联结是一种机制,用来在一条SELECT语句中关联表,因此称之为联结(联结不是物理实体。换句话说,它在实际的数据库表中不存在。联结由MySQL根据需要建立,它存在于查询的执行当中)。
实例:
这里的where限定条件是必须的,否则返回的将是一个笛卡儿积(cartesian product由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数),这也好理解,因为没有限定供应商的id,所以每个供应商都会去和products表中数据结合返回一条数据。
上面我们使用的这种vendors.vend_id = products.vend_id 联结称为等值联结(equijoin),它基于两个表之间的相等测试。这种联结也称为内部联结(INNER JOIN 参考小白python建站的一些准备(五))。
我们使用如下语法也可以实现同样的效果,下图中,两个表之间的关系是FROM子句的组成部分,以INNERJOIN指定。在使用这种语法时,联结条件用特定的ON子句而不是WHERE子句给出。传递给ON的实际条件与传递给WHERE的相同(ANSI SQL规范首选INNER JOIN语法)。
稍微复杂一点的,如查询订单号为20005的客户购买了哪些产品名称,供应商名称,产品价格及数量:
创建高级联结
1. 使用表别名,上述例子简化之后如下:
需要注意的是:表别名只在查询执行中使用(无论前后位置)。与列别名不一样,表别名不返回到客户机。
2.除内部联结外,还有其它三种联结,分别是自联结、自然联结和外部联结。自联结说明:假如你发现某物品(其ID为DTNTR)存在问题,因此想知道生产该物品的供应商生产的其他物品是否也存在这些问题。此查询要求首先找到生产ID为DTNTR的物品的供应商,然后找出这个供应商生产的其他物品。下面是解决此问题的一种方法
这是使用子查询的解决方案,我们可以使用联结的方式试试
此查询中需要的两个表实际上是相同的表,因此products表在FROM子句中出现了两次。虽然这是完全合法的,但对products的引用具有二义性,因为MySQL不知道你引用的是products表中的哪个实例。为解决此问题,使用了表别名。products的第一次出现为别名p1,第二次出现为别名p2。现在可以将这些别名用作表名。例如,SELECT语句使用p1前缀明确地给出所需列的全名。如果不这样,MySQL将返回错误(实测,Mysql8可以返回正确的数据,可能Mysql 5的版本确实有此问题),因为分别存在两个名为prod_id、prod_name的列。MySQL不知道想要的是哪一个列(即使它们事实上是同一个列)。WHERE(通过匹配p1中的vend_id和p2中的vend_id)首先联结两个表,然后按第二个表中的prod_id过滤数据,返回所需的数据。
自联结通常作为外部语句用来替代从相同表中检索数据时使用的子查询语句。虽然最终的结果是相同的,但有时候处理联结远比处理子查询快得多。
3.自然联结。无论何时对表进行联结,应该至少有一个列出现在不止一个表中(被联结的列)。标准的联结(前一章中介绍的内部联结)返回所有数据,甚至相同的列多次出现。自然联结排除多次出现,使每个列只返回一次。用的很少。
4.外部联结。许多联结将一个表中的行与另一个表中的行相关联。但有时候会需要包含没有关联行的那些行。例如,可能需要使用联结来完成以下工作:
- 对每个客户下了多少订单进行计数,包括那些至今尚未下订单的客户
- 列出所有产品以及订购数量,包括没有人订购的产品
- 计算平均销售规模,包括那些至今尚未下订单的客户
上述例子中,联结包含了那些在相关表中没有关联行的行。这种类型的联结称为外部联结(关键词OUTER JOIN)。下面的SELECT语句给出一个简单的内部联结。它检索所有客户及其订单,可以看到10002客户未下单,则内部联结不会将其列出。
外部联结语法类似。为了检索所有客户,包括那些没有订单的客户,可通过OUTER JOIN进行:
可以看到客户10002被找出,尽管其订单为Null,OUTER JOIN指定联结的类型,与内部联结关联两个表中的行不同的是,外部联结还包括没有关联行的行。在使用OUTER JOIN语法时,必须使用RIGHT或LEFT关键字 指定包括其所有行的表(RIGHT指出的是OUTER JOIN右边的表,而LEFT 指出的是OUTER JOIN左边的表)。上例的LEFT选定为FROM子句的左边customers表,所以列出了10002,假如我们指定RIGHT,可以看到客户10002消失了。外联结我的理解是,类比数学的并集,即内部联结之后指定的左、右表的并集
聚集函数的联结
如果要检索所有客户及每个客户所下的订单数,如下
SELECT语句使用INNER JOIN将customers和orders表互相关联。GROUP BY 子句按客户分组数据,函数调用 COUNT (order_num)对每个客户的订单计数,将它作为num_ord返回。这个语句可以分开来看,当我们调用
SELECT COUNT( orders.order_num ) AS num_ord FROM orders> 5
返回的是5个订单,但是调用GROUP BY方法即可将其分类