联结表

本章将介绍什么是联结,为什么要使用联结,如何编写使用联结的SELECT语句。

联结

SQL最强大的功能之一就是能在数据检索查询的执行中联结(join)表。

在能够有效地使用联结前,必须了解关系表以及关系数据库设计的一些基础知识。

关系表

假如有一个包含产品目录的数据库表,其中每种类别的物品占一行。要存储的信息包括产品描述、价格以及供应商的信息。

我们将供应商信息(供应商名、地址、联系方式)另外存储,原因:

  • 同一个供应商生产的每个产品的供应商信息都是相同的,重复存储此信息既浪费时间又浪费存储空间。
  • 如果供应商信息改变,只需改动一次供应商表。
  • 如果有重复数据(每种产品都存储供应商信息),很难保证每次输入该数据的方式都相同。不一致的数据在报表中很难利用。

关系表的设计就是要保证把信息分解成多个表。各表通过某些常用的值(即关系设计中的关系(relational))互相关联。

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

products表只存储产品信息,它除了存储供应商ID(vendors表的主键)外不存储其他供应商信息。vendors表的主键又叫作products的外键,它将vendors表与products表关联。

外键(foreign key) 外键为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。

建两个表的好处:

  • 供应商信息不重复,从而不浪费时间和空间。
  • 如果供应商信息变动,可以只更新vendors表中的单个记录,相关表中的数据不用改动。
  • 由于数据无重复,显然数据是一致的,这使得处理数据更简单。

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

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

为什么要使用联结

分解数据为多个表能更有效地存储,更方便的处理,并且具有更大的可伸缩性。

但数据存储在多个表中,很难用单条SELECT语句检索出数据。这时我们可以使用联结。

联结是一种机制,用来在一条SELECT语句中关联表。使用特殊的语法,可以联结多个表返回同一组输出,联结在运行时关联表中正确的行。

  • 维护引用完整性 联结不是物理实体,它在实际的数据库表中不存在。联结由MySQL根据需要建立,它存在于查询的执行中。
    在使用关系表时,仅在关系列中插入合法的数据非常重要。错误的数据将导致行不可访问。
    为防止错误数据,可指示MySQL只允许products表的供应商ID中出现已存在值(即出现在vendors表中的供应商)。这就是维护引用完整性,它通过表中定义主键和外键来实现。

创建联结

联结创建只需规定要联结的所有表以及它们如何关联即可:

mysql> 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.02 sec)

此语句特殊在于,所指定的两个列(prod_name和prod_price)在一个表中,而另一个列(vend_name)在另一个表中。

这条语句的FROM子句列出来两个表,vendors和products。它们就是联结的两个表的名字。这两个表用WHERE子句正确联结,指示MySQL匹配vendors表中的vend_id和products表中的vend_id。

要匹配的列用完全限定列名vendors.vend_id和products.vend_id指定。如果只给出vend_id,MySQL不知道指哪一个(每个表都有一个vend_id)。

  • 完全限定列名 在引用的列可能出现二义性时,必须使用完全限定列名(用一个点分隔的表名和列名)。

WHERE子句的重要性

利用WHERE子句建立联结关系,原因在于,在一条SELECT语句中联结几个表时,相应的关系是在运行中构造的。

WHERE子句作为过滤条件,它只包含那些匹配给定条件(联结条件)的行。没有WHERE子句,第一个表中的每个行与第二个表中的每个行配对,而不管逻辑上是否正确。

  • 笛卡儿积(cartesian product) 由没有联结条件的表关系返回的结果为笛卡儿积。

vendors表与products表的笛卡儿积:

mysql> 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.00 sec)

笛卡儿积不是我们想要的。返回的每个供应商匹配了每个商品,无论是否是正确的匹配。

  • 不要忘了WHERE子句 应该保证所有联结都有WHERE子句,否则MySQL将返回比想要数据多得多的数据。
  • 叉联结 有时我们会听到返回称为叉联结(cross join)的笛卡儿积的联结类型。

内部联结

前面所学的联结称为等值联结(equijoin),它基于两个表之间的相等测试。这也被称为內部联结。而内部联结有不同的语法:

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 |
+-------------+----------------+------------+
| Anvils R Us | .5 ton anvil   |       5.99 |
| Anvils R Us | 1 ton anvil    |       9.99 |
| Anvils R Us | 2 ton anvil    |      14.99 |
| LT Supplies | Fuses          |       3.42 |
| LT Supplies | Oil can        |       8.99 |
| ACME        | Detonator      |      13.00 |
| ACME        | Bird seed      |      10.00 |
| ACME        | Carrots        |       2.50 |
| ACME        | Safe           |      50.00 |
| ACME        | Sling          |       4.49 |
| ACME        | TNT (1 stick)  |       2.50 |
| ACME        | TNT (5 sticks) |      10.00 |
| Jet Set     | JetPack 1000   |      35.00 |
| Jet Set     | JetPack 2000   |      55.00 |
+-------------+----------------+------------+
14 rows in set (0.01 sec)

此SELECT语句与前文功能一致,但FROM子句不同。FROM子句的组成是两个表的关系,以INNER JOIN指定。联结条件用ON子句而不是WHERE给出。

  • 使用哪种语法 ANSI SQL规范首选INNER JOIN语法。

联结多个表

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

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 = 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.01 sec)

此例子显示编号为20005的订单中的物品。订单通过供应商ID联结到vendors表中的供应商,通过产品ID联结到products表中的产品。

  • 性能考虑 MySQL在运行时关联指定的每个表以处理联结。这种处理可能非常耗费资源,因此,不用联结不必要的表。

回顾十四章(MySQL必知必会——第十四章使用子查询)的例子,返回订购产品TNT2的客户列表:

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 = 'TNT2'));
+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
+----------------+--------------+
2 rows in set (0.01 sec)

子查询不总是执行复杂SELECT操作的最有效的方法,有时联结可能更加合适:

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 = 'TNT2';
+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
+----------------+--------------+
2 rows in set (0.01 sec)
  • 多做实验 为执行任一给定的SQL操作,一般存在不止一种方法。很少有绝对正确或绝对错误的方法。性能可能会受操作类型、表中数据量、是否存在索引或键以及其他的一些条件的影响。因此,我们有必要对不同的选择机制进行实验,以找出最适合具体情况的方法。

MySQL必知必会——第十五章联结表相关推荐

  1. 《MySQL必知必会》学习笔记十(增删改语句使用)------掌握部分

    MySQL必知必会知识预览 第一章--了解SQL 第二章--MySQL简介 第三章--使用MySQL 第四章--检索数据 第五章--排序检索数据 第六章--过滤数据 第七章--数据过滤 第八章--用通 ...

  2. MySQL必知必会笔记(一)基础知识和基本操作

    第一章  了解MySQL     数据库       保存有组织的数据的容器.(通常是一个文件或一组文件) 人们经常使用数据库这个术语代替他们使用的软件.这是不正确的,确切的说,数据库软件应称为DBM ...

  3. MySQL必知必会pdf网盘下载+每章总结

    书籍下载 链接:https://pan.baidu.com/s/18g0XgavniXQzp6RlOxnwnQ 密码:j8pm 目录 书籍下载 MySQL必知必会 第二章 连接mysql 第三章 连接 ...

  4. 读书笔记系列1——MySQL必知必会

    读书笔记系列1--MySQL必知必会 文章目录 读书笔记系列1--MySQL必知必会 MySQL官方文档:https://dev.mysql.com/doc/ 第一章 数据库基础 *2021.11.2 ...

  5. 30 分钟读完《MySQL 必知必会》

    第一章 了解 SQL 介绍几个数据库的概念 数据库(database) 通过DBMS创建和操纵的保存有组织的数据的容器(通常是一个文件或一组文件). 数据库软件/数据库管理系统(DBMS)访问数据库, ...

  6. mysql必学十大必会_MYSQL 学习(一)--启蒙篇《MYSQL必知必会》

    MYSQL必知必会 一. DDL 数据定义语言 Data Definition Language 是指CREATE,ALTER和DROP语句. DDL允许添加/修改/删除包含数据的逻辑结构,或允许用户 ...

  7. mysql函桌为之一的_MYSQL必知必会读书笔记第十和十一章之使用函数处

    mysql简介 MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),MySQL数据库系统使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管理. 拼接字段 存储在数据库表中的 ...

  8. 根据SQL必知必会学习SQL(MYSQL)

    很久都没有整理SQL语句了,遇到让写SQL语句的题也很迷茫,所以就重拾一下知识,本文章根据SQL必知必会进行梳理 文章目录 一.检索所有列 1.select 1.1检索单个列 1.2 检索多个列 1. ...

  9. MYSQL必知必会学习笔记(二)

    MYSQL必知必会四-五章--检索.排序 书中部分代码展示: ##第四第五章 SELECT prod_name FROM products; /*从products表中检索一个叫prod_name的列 ...

最新文章

  1. dm-haiku 用法
  2. 【cocos2d-x从c++到js】16:使用cocos2d-console工具转换脚本为字节码
  3. Java数据类型转换全解
  4. 道路检测 | SNE-RoadSeg论文阅读
  5. C++中max,min函数,以及vector字符未定义问题(未声明的标志符)
  6. boost::thread_group相关的测试程序
  7. 商丘高中计算机考试成绩查询系统,2019商丘中考招生成绩查询时间及网站公布...
  8. 帝国cms搜索php分页,帝国cms V7.2自定义分页(列表分页,内容分页)样式步骤
  9. NAPI 方式的实现
  10. 前端网络基础-GET和POST的区别
  11. 从六个方面讲解抢购软件需要具备的条件
  12. If you insist running as root, then set the environment variable RUN_AS_USER=root
  13. python3读取键盘输入_Python读取键盘输入
  14. 循环小数(Repeating Decimals, ACM/ICPC World Finals 1990, UVa202)
  15. Machine Learning | (1) Scikit-learn与特征工程
  16. 大气幼儿园html模板,幼儿园儿童HTML主题
  17. Cadence Allegro解散Group组图文教程及视频演示
  18. Git-fatal: unable to access ‘https://gitlab.XX.git/‘: Could not resolve host: gitlab.XX.com.cn
  19. Xcode10 报错 target xxx(project 'xxx')has copy command from ‘xxx/xxx’ to ''xxx/xxx.app/COPYING'解决方案
  20. android getruntime.exec 权限

热门文章

  1. 解析Activity的启动过程
  2. 安装PS出现“由于找不到VCRUNTIME 140_1.dll”的解决方法
  3. 微信聊天记录内的文件如何实现自动同步备份?
  4. 【工作感悟】老程序员总结的四条工作经验教训
  5. 微分几何学习(二)(曲线论,弧微分)
  6. mybatis mysql ssh_SSH Mybatis 框架
  7. kubeadm快速部署一套K8S集群
  8. Jetson Nano | darknet (yolov3.4-tiny)摄像头实时检测
  9. 如何把一个web网站做成一个window应用程序
  10. 怎么把文字转换成朗读,快来看这几款文字转语音朗读工具