查询需求闻风而来,联表查询知多少?逐步解剖它

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 查询需求闻风而来,联表查询知多少?逐步解剖它

关于连接查询

  • 在关系型数据库中,表与表之间是有联系的,所以在实际应用中,经常使用多表查询,多表查询就是同时查询两个或两个以上的表。
  • 多表查询一般使用内连接和外连接,它们的效率要高于交叉连接。

一、交叉连接

  • 交叉连接(CROSS JOIN)一般用来返回连接表的笛卡尔积。

1. 笛卡尔积

user表

hosts表

在mysql中,笛卡尔积的写法为

select * from A,B # 大部分的写法,且推荐
# 或者
select * from A cross join B

到此为止还是没说清楚笛卡尔积到底是什么鬼

  • 笛卡尔积(Cartesian product)是指两个集合 X 和 Y 的乘积。例如,有 A 和 B 两个集合,它们的值如下:
A = {1,2}
B = {3,4,5}

集合 A×B 和 B×A 的结果集分别表示为:

A×B={(1,3), (1,4), (1,5), (2,3), (2,4), (2,5) };
B×A={(3,1), (3,2), (4,1), (4,2), (5,1), (5,2) };

以上 A×B 和 B×A 的结果就叫做两个集合的笛卡尔积。

并且,从以上结果我们可以看出:

  • 两个集合相乘,不满足交换率,即 A×B≠B×A。
  • A 集合和 B 集合的笛卡尔积是 A 集合的元素个数 × B 集合的元素个数。

多表查询遵循的算法就是以上提到的笛卡尔积,表与表之间的连接可以看成是在做乘法运算。在实际应用中,应避免使用笛卡尔积,因为笛卡尔积中容易存在大量的不合理数据,简单来说就是容易导致查询结果重复、混乱。

再回到user表和hosts表

将user表和hosts表转化成2个集合

# user集合
{
    (ttr1,Admin),
  (ttr2,Admin),
  (ttr3,Admin),
  (ttr4,Admin)
}
# hosts集合
{
  (web01,192.168.11.10),
  (web02,192.168.11.11),
  (web03,192.168.11.12),
  (web04,192.168.11.13),
  (web05,192.168.11.14),
  (web06,192.168.11.15),
  (web07,192.168.11.16)
}

两个集合的笛卡尔积为

笛卡尔(Descartes)乘积又叫直积。再次巩固一下概念,假设集合A={a,b},集合B={0,1,2},则两个集合的笛卡尔积为{(a,0),(a,1),(a,2),(b,0),(b,1), (b,2)},遵循笛卡尔积的算法,就可以知道user集合、hosts集合的笛卡尔积是多少了。

类似的例子有,如果A表示某学校学生的集合,B表示该学校所有课程的集合,则A与B的笛卡尔积表示所有可能的选课情况。

2. 交叉连接查询案例

案例:使用 CROSS JOIN 查询出两张表中的笛卡尔积,SQL 语句和运行结果如下:

mysql> select * from zabbix.items, zabbix.hosts;

由运行结果可以看出,items 和 hosts 表交叉连接查询后,返回了 N 条记录。可以想象,当表中的数据较多时,得到的运行结果会非常长,而且得到的运行结果也没太大的意义。所以,通过交叉连接的方式进行多表查询的这种方法并不常用,我们应该尽量避免这种查询。

案例:查询 items 表中的 hostid 字段和 hosts 表中的 hostid 字段相等、且hosts表的status字段等于0的内容, SQL 语句和运行结果如下:

select i.name,h.name from zabbix.items as i cross join zabbix.hosts as h where h.status=0 and i.hostid=h.hostid;
# 或
select i.name,h.name from zabbix.items as i, zabbix.hosts as h where h.status=0 and i.hostid=h.hostid;

如果在交叉连接时使用 WHERE 子句,MySQL 会先生成两个表的笛卡尔积,然后再选择满足 WHERE 条件的记录。因此,表的数量较多时,交叉连接会非常非常慢。一般情况下不建议使用交叉连接。在 MySQL 中,多表查询一般使用内连接和外连接,它们的效率要高于交叉连接。


二、内连接

内连接(INNER JOIN)主要通过设置连接条件的方式,来移除查询结果中某些数据行的交叉连接。简单来说,就是利用条件表达式来消除交叉连接的某些数据行。

内连接使用 INNER JOIN 关键字连接两张表,并使用 ON 子句来设置连接条件。如果没有连接条件,INNER JOIN 和 CROSS JOIN 在语法上是等同的,两者可以互换。

案例:在 hosts 表和 interface 表之间,使用内连接查询主机名称和相对应的ip地址,SQL 语句和运行结果如下。

mysql> select h.name,i.ip from zabbix.hosts h inner join zabbix.interface i on h.hostid=i.hostid;       
+--------------+----------------+
| name         | ip             |
+--------------+----------------+
| zbxser01     | 192.168.11.153 |
| mysql-master | 192.168.11.151 |
| mysql-db02   | 192.168.11.152 |
| zbxser02     | 192.168.11.154 |
| zbxproxy01   | 192.168.11.155 |
| zbxproxy02   | 192.168.11.156 |
| zbxproxy04   | 192.168.11.158 |
| zbxproxy03   | 192.168.11.157 |
+--------------+----------------+
8 rows in set (0.00 sec)
# inner可省略
mysql> select h.name,i.ip from zabbix.hosts h join zabbix.interface i on h.hostid=i.hostid;

在这里的查询语句中,两个表之间的关系通过 INNER JOIN 指定,连接的条件使用 ON 子句给出。

注意:当对多个表进行查询时,要在 SELECT 语句后面指定字段是来源于哪一张表。因此,在多表查询时,SELECT 语句后面的写法是表名.列名。另外,如果表名非常长的话,也可以给表设置别名,这样就可以直接在 SELECT 语句后面写上表的别名.列名。

案例:使用内连接查询主机和相对应的监控模板名称,并进行分组显示

select h.name,group_concat(i.name) from zabbix.hosts h inner join zabbix.items i on h.status=0 and h.hostid=i.hostid group by h.name\G;

INNER JOIN 也可以使用 WHERE 子句指定连接条件,但是 INNER JOIN ... ON 语法是官方的标准写法,而且 WHERE 子句在某些时候会影响查询的性能。

多个表内连接时,在 FROM 后连续使用 INNER JOIN 或 JOIN 即可。


三、外连接

  • 外连接会先将连接的表分为基表和参考表,再以基表为依据返回满足和不满足条件的记录。
  • 外连接可以分为左外连接和右外连接

1. 左外连接

左外连接又称为左连接,使用 LEFT OUTER JOIN 关键字连接两个表,并使用 ON 子句来设置连接条件。

左连接的语法格式如下:

SELECT <字段名> FROM <表1> LEFT OUTER JOIN <表2> <ON子句>

语法说明如下:

  • 字段名:需要查询的字段名称。
  • <表1><表2>:需要左连接的表名。
  • LEFT OUTER JOIN:左连接中可以省略 OUTER 关键字,只使用关键字 LEFT JOIN。
  • ON 子句:用来设置左连接的连接条件,不能省略。

上述语法中,“表1”为基表,“表2”为参考表。左连接查询时,可以查询出“表1”中的所有记录和“表2”中匹配连接条件的记录。如果“表1”的某行在“表2”中没有匹配行,那么在返回结果中,“表2”的字段值均为空值(NULL)。

案例:在 hosts 表和 interface 表中查询所有主机名称和相对应的ip地址,包括没有ip地址的模板名称,SQL 语句和运行结果如下:

  • 以hosts表为基表,interface表为参照表
mysql> select h.name,i.ip from zabbix.hosts h left join zabbix.interface i on h.hostid=i.hostid;
+-----------------------------------------------------------------------------+----------------+
| name                                                                        | ip             |
+-----------------------------------------------------------------------------+----------------+
...
| Template VM VMware                                                          | NULL           |
| Template VM VMware Guest                                                    | NULL           |
| Template VM VMware Hypervisor                                               | NULL           |
| Template VM VMware macros                                                   | NULL           |
| mysql-db02                                                                  | 192.168.11.152 |
| mysql-master                                                                | 192.168.11.151 |
| zbxproxy01                                                                  | 192.168.11.155 |
| zbxproxy02                                                                  | 192.168.11.156 |
| zbxproxy03                                                                  | 192.168.11.157 |
| zbxproxy04                                                                  | 192.168.11.158 |
| zbxser01                                                                    | 192.168.11.153 |
| zbxser02                                                                    | 192.168.11.154 |
| {#HV.NAME}                                                                  | NULL           |
| {#VM.NAME}                                                                  | NULL           |
+-----------------------------------------------------------------------------+----------------+
...

可以看到,第1条记录Template VM VMware 没有 ip地址(它其实是模板名称),因为对应的 interface 表中没有该“主机”的ip地址信息,所以该条记录只取出了 hosts 表中相应的值“Template VM VMware”,而从 interface 表中取出的值为 NULL。

2. 右外连接

右外连接又称为右连接,右连接是左连接的反向连接。使用 RIGHT OUTER JOIN 关键字连接两个表,并使用 ON 子句来设置连接条件。

右连接的语法格式如下:

SELECT <字段名> FROM <表1> RIGHT OUTER JOIN <表2> <ON子句>

语法说明如下。

  • 字段名:需要查询的字段名称。
  • <表1><表2>:需要右连接的表名。
  • RIGHT OUTER JOIN:右连接中可以省略 OUTER 关键字,只使用关键字 RIGHT JOIN。
  • ON 子句:用来设置右连接的连接条件,不能省略。

与左连接相反,右连接以“表2”为基表,“表1”为参考表。右连接查询时,可以查询出“表2”中的所有记录和“表1”中匹配连接条件的记录。如果“表2”的某行在“表1”中没有匹配行,那么在返回结果中,“表1”的字段值均为空值(NULL)。

案例:在 hosts 表和 interface 表中查询所有主机名称和相对应的ip地址,包括没有ip地址的模板名称,SQL 语句和运行结果如下:

  • 以interface表为基表,hosts表为参照表
mysql> select h.name,i.ip from zabbix.hosts h right join zabbix.interface i on h.hostid=i.hostid;    
+--------------+----------------+
| name         | ip             |
+--------------+----------------+
| zbxser01     | 192.168.11.153 |
| mysql-master | 192.168.11.151 |
| mysql-db02   | 192.168.11.152 |
| zbxser02     | 192.168.11.154 |
| zbxproxy01   | 192.168.11.155 |
| zbxproxy02   | 192.168.11.156 |
| zbxproxy04   | 192.168.11.158 |
| zbxproxy03   | 192.168.11.157 |
+--------------+----------------+
8 rows in set (0.00 sec)
mysql>

注意:使用外连接查询时,一定要分清需要查询的结果,是需要显示左表的全部记录还是右表的全部记录,然后选择相应的左连接和右连接。

多个表左/右连接时,在 ON 子句后连续使用 LEFT/RIGHT OUTER JOIN 或 LEFT/RIGHT JOIN 即可。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7月前
|
存储 关系型数据库 MySQL
提高查询性能的秘密:深入剖析聚集、辅助、覆盖和联合索引
提高查询性能的秘密:深入剖析聚集、辅助、覆盖和联合索引
115 0
|
7天前
|
存储 Oracle 关系型数据库
索引在手,查询无忧:MySQL索引简介
MySQL 是一款广泛使用的关系型数据库管理系统,在2024年5月的DB-Engines排名中得分1084,仅次于Oracle。本文介绍MySQL索引的工作原理和类型,包括B+Tree、Hash、Full-text索引,以及主键、唯一、普通索引等,帮助开发者优化查询性能。索引类似于图书馆的分类系统,能快速定位数据行,极大提高检索效率。
34 8
|
6月前
|
SQL 关系型数据库 MySQL
从理论到实践,Mysql查询优化剖析(联表查询)
从理论到实践,Mysql查询优化剖析(联表查询)
254 0
|
4月前
|
SQL 数据挖掘 数据库
SQL 子查询深度剖析来袭!嵌套查询竟有如此无限可能,带你轻松玩转复杂数据检索与操作!
【8月更文挑战第31天】在 SQL 中,子查询是一种强大的工具,允许在一个查询内嵌套另一个查询,从而实现复杂的数据检索和操作。子查询分为标量子查询、列子查询和行子查询,可用于 SELECT、FROM、WHERE 和 HAVING 子句中。例如,查找年龄大于平均年龄的学生或每个课程中成绩最高的学生。子查询具有灵活性、可重用性和潜在的性能优化优势,但需注意性能问题、可读性和数据库支持。合理使用子查询能够显著提升查询效率和代码维护性。
130 0
|
7月前
|
存储 关系型数据库 MySQL
表设计的10条军规
本文主要介绍了数据库建表的18个小技巧,包括:名字的命名规范、字段类型的选取、字段长度的控制、外键的使用、索引的创建、主键的选择、字段个数的限制、存储引擎的选择、时间字段的处理、金额字段的保存、冗余字段的使用以及注释的添加。作者强调了命名的重要性,如使用小写字母、避免全大写、使用下划线分隔等,并提倡使用NOT NULL和默认值,合理选择字段类型如datetime、decimal等,以及避免使用过多的字段和索引。此外,还提到了字符集和排序规则的选择,以及大字段和冗余字段的处理。
212 1
|
7月前
|
关系型数据库 MySQL 定位技术
解谜MySQL索引:优化查询速度的不二法门
解谜MySQL索引:优化查询速度的不二法门
72 0
|
7月前
|
存储 SQL JSON
晓得不,中间表是这样被消灭的
晓得不,中间表是这样被消灭的
|
关系型数据库 MySQL 数据库
DAMA试题分析:主键有什么特性?
DAMA试题分析:主键有什么特性?
机房收费系统—组合查询逻辑分析
机房收费系统—组合查询逻辑分析
|
SQL 缓存 数据库
SQL优化小讲堂(一)——不查询多余的列和行
从今天开始,每天会给大家普及一点优化的小技巧。感兴趣的同学可以跟着多多练练手,示例数据库可以在阅读原文处获取。我使用的示例是AdventureWorks2012的备份包,小伙伴下载后还原即可。记得数据库要是高版本的才能还原成功,我使用的数据库是SQL Server 2016版本的。
SQL优化小讲堂(一)——不查询多余的列和行