MySQL必知必会——第十六章创建高级联结

2023-11-04

创建高级联结

本章将讲解另外一些联结类型(包括它们的含义和使用方法),介绍如何对被联结的表使用表别名和聚集函数。

使用表别名

第十章(MySQL必知必会——第十章创建计算字段)介绍了如何使用别名引用表列:

mysql> SELECT Concat(RTrim(vend_name), ' (', RTrim(vend_country), ')') AS vend_title
    -> FROM vendors
    -> ORDER BY vend_name;
+-------------------------+
| vend_title              |
+-------------------------+
| ACME (USA)              |
| Anvils R Us (USA)       |
| Furball Inc. (USA)      |
| Jet Set (England)       |
| Jouets Et Ours (France) |
| LT Supplies (USA)       |
+-------------------------+
6 rows in set (0.00 sec)

别名除了用于表列和计算字段外,SQL还允许给表名取别名,优点:

  • 缩短SQL语句。
  • 允许在单条SELECT语句中多次使用同一张表。

对前一章(MySQL必知必会——第十五章联结表)的例子使用表别名:

mysql> SELECT cust_name, cust_contact
    -> FROM customers AS c, orders AS o, orderitems AS oi
    -> WHERE c.cust_id = o.cust_id
    -> AND oi.order_num = o.order_num
    -> AND prod_id = 'TNT2';
+----------------+--------------+
| cust_name      | cust_contact |
+----------------+--------------+
| Coyote Inc.    | Y Lee        |
| Yosemite Place | Y Sam        |
+----------------+--------------+
2 rows in set (0.03 sec)

FROM子句中的三个表全部都具有表别名。这使得能省写而不是写全名。表别名不仅能用于WHERE子句,还可以用于SELECT的列表,ORDER BY子句及其他语句部分。

表别名只在查询执行中使用,不返回到客户机。


使用不同类型的联结

到目前我们使用的都是称为内部联结或等值联结(equijoin)的简单联结。还其他的三种联结,分别为自联结、自然联结和外部联结。

自联结

前面所说,别名的一个优点是允许在单条SELECT语句中多次使用同一张表。

假如发现某件物品(如ID为DTNTR)存在问题,因此想知道该物品生产商的其他物品是否同样存在问题。下面是一种查找该物品生产商的所有物品的方法:

mysql> SELECT prod_id, prod_name
    -> FROM products
    -> WHERE vend_id = (SELECT vend_id
    ->                  FROM products
    ->                  WHERE prod_id = 'DTNTR');
+---------+----------------+
| prod_id | prod_name      |
+---------+----------------+
| DTNTR   | Detonator      |
| FB      | Bird seed      |
| FC      | Carrots        |
| SAFE    | Safe           |
| SLING   | Sling          |
| TNT1    | TNT (1 stick)  |
| TNT2    | TNT (5 sticks) |
+---------+----------------+
7 rows in set (0.01 sec)

这是第一种方法,使用子查询(MySQL必知必会——第十四章使用子查询)。首先查出供应商的ID,再根据供应商的ID查出该供应商的物品。

还有一种解决方法是使用联结:

mysql> SELECT p1.prod_id, p1.prod_name
    -> FROM products AS p1, products AS p2
    -> WHERE p1.vend_id = p2.vend_id
    -> AND p2.prod_id = 'DTNTR';
+---------+----------------+
| prod_id | prod_name      |
+---------+----------------+
| DTNTR   | Detonator      |
| FB      | Bird seed      |
| FC      | Carrots        |
| SAFE    | Safe           |
| SLING   | Sling          |
| TNT1    | TNT (1 stick)  |
| TNT2    | TNT (5 sticks) |
+---------+----------------+
7 rows in set (0.00 sec)

此查询中的两个表是同一个表。但因为有二义性,所以我们要使用表别名才能正常使用两张表。

  • 用自联结而不用子查询 自联结通常作为外部语句用来替代从相同表中检索数据时使用的子查询语句。虽然结果相同,但有时联结的速度比子查询快得多。

自然联结

对表进行联结,应该保证至少有一个列出现在不止一个表中(被联结的列)。标准的联结返回所有数据,甚至相同的列多次出现。自然联结排除多次出现,使每个列只返回一次

但,系统不完成这项工作,这由你自己完成。自然联结一般通过对表使用通配符(SELECT *) ,对所有其他表的列使用明确的子集完成。

mysql> SELECT c.*, o.order_num, o.order_date, oi.prod_id, oi.quantity, oi.item_price
    -> FROM customers AS c, orders AS o, orderitems AS oi
    -> WHERE c.cust_id = o.cust_id
    -> AND oi.order_num = o.order_num
    -> AND prod_id = 'FB';
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+
| cust_id | cust_name   | cust_address   | cust_city | cust_state | cust_zip | cust_country | cust_contact | cust_email      | order_num | order_date          | prod_id | quantity | item_price |
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+
|   10001 | Coyote Inc. | 200 Maple Lane | Detroit   | MI         | 44444    | USA          | Y Lee        | ylee@coyote.com |     20005 | 2005-09-01 00:00:00 | FB      |        1 |      10.00 |
|   10001 | Coyote Inc. | 200 Maple Lane | Detroit   | MI         | 44444    | USA          | Y Lee        | ylee@coyote.com |     20009 | 2005-10-08 00:00:00 | FB      |        1 |      10.00 |
+---------+-------------+----------------+-----------+------------+----------+--------------+--------------+-----------------+-----------+---------------------+---------+----------+------------+
2 rows in set (0.01 sec)

这个例子中,没有重复的列。

目前我们建立的联结都是自然联结,以后也可能都碰不到非自然联结。

外部联结

许多联结将一张表的行与另一张表的行相关联。但有时我们需要没有关联的行。例如:

  • 对每个客户下了多少订单进行计数,包括没下订单的客户。
  • 列出所有产品以及订购数量,包括没人订购的产品。
  • 计算平均销售规模,包括未下订单的客户。

联结包含在相关表中没有关联行的行,称为外部联结。

检索所有订单和客户的内部联结:

mysql> SELECT customers.cust_id, orders.order_num
    -> FROM customers INNER JOIN orders
    -> ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
5 rows in set (0.01 sec)

使用外部联结,检索所有客户,包括未下订单的客户:

mysql> SELECT customers.cust_id, orders.order_num
    -> FROM customers LEFT OUTER JOIN orders
    -> ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10002 |      NULL |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
6 rows in set (0.00 sec)

此SELECT语句使用OUTER JOIN关键字来指定联结的类型,而非WHERE中指定。与内部联结不同的是,外部联结包含了客户10002,而客户10002并未与任何订单相关联。在使用OUTER JOIN语法时,需要使用LEFT或RIGHT指定需要包含所有行的表,LEFT指定关键字左边的表,RIGHT指定关键字右边的表。

选定右边表的所有行:

mysql> SELECT customers.cust_id, orders.order_num
    -> FROM customers RIGHT OUTER JOIN orders
    -> ON customers.cust_id = orders.cust_id;
+---------+-----------+
| cust_id | order_num |
+---------+-----------+
|   10001 |     20005 |
|   10001 |     20009 |
|   10003 |     20006 |
|   10004 |     20007 |
|   10005 |     20008 |
+---------+-----------+
5 rows in set (0.00 sec)
  • 没有*=操作符 MySQL不支持简化字符*=和=*的使用。
  • 外部联结的类型 存在两种基本的外部联结形式:左外部联结和右外部联结。它们唯一差别是所关联的表顺序不同。左外部联结可通过颠倒表的顺序转换为右外部联结。

使用带聚集函数的联结

聚集函数(MySQL必知必会——第十二章汇总数据)用来汇总数据。虽然目前的例子都是从单张表中汇总数据,但可以在联结中使用汇总函数。

检索所有客户及客户下单数:

mysql> SELECT customers.cust_name,
    ->        customers.cust_id,
    ->        COUNT(orders.order_num) AS num_ord
    -> FROM customers INNER JOIN orders
    -> ON customers.cust_id = orders.cust_id
    -> GROUP BY customers.cust_id;
+----------------+---------+---------+
| cust_name      | cust_id | num_ord |
+----------------+---------+---------+
| Coyote Inc.    |   10001 |       2 |
| Wascals        |   10003 |       1 |
| Yosemite Place |   10004 |       1 |
| E Fudd         |   10005 |       1 |
+----------------+---------+---------+
4 rows in set (0.01 sec)

聚集函数也可以方便地与其他联结一起使用:

mysql> SELECT customers.cust_name,
    ->        customers.cust_id,
    ->        COUNT(orders.order_num) AS num_ord
    -> FROM customers LEFT OUTER JOIN orders
    -> ON customers.cust_id = orders.cust_id
    -> GROUP BY customers.cust_id;
+----------------+---------+---------+
| cust_name      | cust_id | num_ord |
+----------------+---------+---------+
| Coyote Inc.    |   10001 |       2 |
| Mouse House    |   10002 |       0 |
| Wascals        |   10003 |       1 |
| Yosemite Place |   10004 |       1 |
| E Fudd         |   10005 |       1 |
+----------------+---------+---------+
5 rows in set (0.01 sec)

此例子使用外部联结显示所有的客户,包括没有订单的客户。


使用联结和联结条件

关于联结及其使用的要点:

  • 注意所使用的联结类型。一般使用內部联结,但有时使用外部联结。
  • 保证使用正确的联结条件,否则将返回不正确的数据。
  • 应该总是提供联结条件,否则会得出笛卡儿积。
  • 在一个联结中可以包含多个表,甚至对于每个联结可以采用不同的联结类型。但应该对每个联结进行单独测试,从而方便故障的排除。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL必知必会——第十六章创建高级联结 的相关文章

  • SQL:比较不同表中的两个计数

    我有 3 张桌子 一张桌子上有世界上每个国家及其代币 NAME CODE Afghanistan AFG Albania AL Algeria DZ American Samoa AMSA Andorra AND Angola ANG An
  • PHP:使用输入和输出参数(不是“INOUT”)调用 MySQL 存储过程

    我想从 PHP 调用 MySQL 中的存储过程 该过程需要输入and输出参数 not INOUT 参数 举一个简单的例子 假设我在 MySQL 中有以下存储过程 DELIMITER DROP PROCEDURE IF EXISTS test
  • 混合语言源目录布局

    我们正在运行一个使用多种不同语言的大型项目 Java Python PHP SQL 和 Perl 到目前为止 人们一直在自己的私有存储库中工作 但现在我们希望将整个项目合并到一个存储库中 现在的问题是 目录结构应该是什么样的 我们应该为每种
  • 如何在 Windows 上安装 PHP 的 PDO 驱动程序?

    我在 Windows 服务器上安装了 Apache PHP 5 6 和 MySQL 5 7 在 php ini 中 我启用了以下内容 extension php mysql dll extension php mysqli dll exte
  • Oracle 获取列值发生变化的行

    假设我有一张桌子 比如 ID CCTR DATE 1 2C 8 1 2018 2 2C 7 2 2018 3 2C 5 4 2017 4 2B 3 2 2017 5 2B 1 1 2017 6 UC 11 23 2016 还有其他字段 但我
  • 在 plsql 中立即执行

    如何从这段代码中得到结果 EXECUTE IMMEDIATE SELECT FROM table name through for loop 通常的方法看起来像这样 for items in select from this table l
  • 基于列顺序的查询速度

    数据库中列类型的顺序对查询时间有影响吗 例如 具有混合排序 INT TEXT VARCHAR INT TEXT 的表的查询速度是否会比具有连续类型 INT INT VARCHAR TEXT TEXT 的表慢 答案是肯定的 这确实很重要 而且
  • 为 Mariadb 安装连接器 C

    所以 我想使用 Mariadb 有一个连接器 C https downloads mariadb org connector c https downloads mariadb org connector c 我该如何安装它 坦白说 它的文
  • 如何在 WP_Query 中按日期排序?

    我已经尝试过这种方式但是orderby and order不适用于 WP Query 类 posts new WP Query array post type gt block code orderby gt post date order
  • 使用 mysql2 gem 获取最后插入的 id

    我有这样的代码 require mysql2 db query insert into clients Name values client 我可以通过 1 个查询返回最后插入的 ID 吗 您可以使用last id客户端实例的方法 clie
  • (SQL) 识别字段中字符串格式多次出现的位置

    我需要将叙述字段 自由文本 拆分为多行 目前的格式如下 Case Reference Narrative XXXX XX 123456 Endless Text up to 50k characters 在作为文本的叙述字段中 各个条目 当
  • 在同一个表上组合两个 SQL SELECT 语句

    我想结合这两个 SQL 查询 SELECT FROM Contracts WHERE productType RINsell AND clearTime IS NULL AND holdTime IS NOT NULL ORDER BY g
  • 是否可以使用“WHERE”子句来选择SQL语句中的所有记录?

    晚上好 我很好奇是否可以在 SQL 语句中创建一个 WHERE 子句来显示所有记录 下面一些解释 随机 SQL 语句 Java JSP示例 正常情况 String SqlStatement SELECT FROM table example
  • MySQL 中的断言

    我有一个针对大型数据库运行的 SQL 脚本 我想在开始时提出几个简单的查询 作为健全性检查 有没有办法在MySQL中写断言 或者任何类型的 选择 如果它与该值不匹配 则中止整个脚本 一些疯狂的代码 要点是 SET可能会引发 mysql 变量
  • 从数据库生成 XML 时出现 PHP 编码错误 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我正在尝试获取一个简单的 PHP 服
  • Spark SQL/Hive 查询通过 Join 永远持续下去

    所以我正在做一些应该很简单的事情 但显然它不在 Spark SQL 中 如果我在 MySQL 中运行以下查询 查询将在不到一秒的时间内完成 SELECT ua address id FROM user u inner join user a
  • 将列的值添加到 LIKE 语句中?

    我有 3 个标签表 标签类别和使用过的标签 我想要获取所有标签的列表以及已使用标签的计数 所使用标签的格式是每个具有标签的文档 ID 的逗号分隔值 我一直在尝试类似的方法 但无法将tags tag 字段的值插入到LIKE 语句中 SELEC
  • 在 MySQL 中分割逗号分隔值

    我正在尝试将字符串中以逗号分隔的 值拆分为多列 样本数据 COL1 COL2 COL3 000002 000003 000042 09 31 51 007 004 007 预期输出 Pno Cno Sno 000002 09 007 000
  • 更改Docker容器中的mysql密码

    我如何更改 docker 容器中的 root 密码 因为一旦我停止 mysql 服务 容器就会自动停止 我应该停止 mysql 容器并部署一个新容器吗 您可以使用正在运行的容器更改它docker exec session https doc
  • MySQL“选择更新”行为

    根据 MySql 文档 MySql 支持多粒度锁定 MGL case 1 开放航站楼 1 连接到mysql mysql gt start transaction Query OK 0 rows affected 0 00 sec mysql

随机推荐