概述
本章,您将学习 MySQL 中有关 DQL 的知识。
在 MySQL 基础篇的整个知识架构中,DQL 的内容最多也最常使用,学习时应该重点掌握与训练。
DQL 主要包含以下内容:
- 条件查询
- 排序查询
- 函数
- 连接查询
- 嵌套查询
- 分页查询
- 联合查询
本文档是 DQL 内容的最后一章,即带读者学习嵌套查询、分页查询和联合查询。
嵌套查询
在不同的文档中,嵌套查询也称子查询或父查询,指的是在一条 SQL 语句中同时出现至少两个 select
关键字的查询,这其中涉及多张临时表之间的互相嵌套,因此被称为嵌套查询。
比如:
select
*
from
(
select
*
from
city
where
name not in("kabul", "qandahar")
) as newtable
where
name = "herat";
不仅查询可以嵌套,数据的修改、新增、删除也是可以被嵌套的。
在嵌套查询中,我们定义:
- 把结果集只有一行一列的称为 标量子查询(也称 单行子查询)
- 把一列多行的结果集称为 列子查询
- 把一行多列或者多行多列的结果集称为 行子查询
单行子查询
在 where
关键字后面加上条件判断,例如这样的嵌套select .... from .... where id>(select ... from ... );
如您所见,我们可以把单行子查询的结果集作为条件判断的值来对待。
列子查询
可以使用 in
、any
、all
等关键字:
- 当用
in
关键字时,表示等于子查询的任意一个,即多个 or 的条件查询 - 当用
any
关键字时,表示和子查询的任意一个值做比较,例如select .... from ... where id>any(select ...);
- 当用
all
关键字时,表示和子查询的所有值做比较,即多个 and 的条件查询。
在 select
语法中,any
和 all
关键字都必须搭配条件表达式进行使用。条件表达式即:
>
– 表示大于<
– 表示小于>=
– 表示大于等于<=
– 表示小于等于<>
– 表示不等于<=>
– 表示安全等于,不太常用=
– 表示等于
三者的对比:
关键字 | 比较方式 | 逻辑关系 | null 值时 | 空值的处理 |
---|---|---|---|---|
in |
等于比较 | or | 无影响 | 返回 false |
any |
搭配条件表达式 | or | 返回 null | 返回 null |
all |
搭配条件表达式 | and | 返回 null | 返回 true |
嵌套查询示例
要求:查询大于 Aruba 国家 GNP 的亚洲国家有哪些,且需要这些国家对应的城市名称
思路:
第一步:查询 Aruba 国家的GNP (select GNP from country where name='Aruba';
)
第二步:将第一步查询出来的值作为比较值并筛选出具体的亚洲国家名称(select name from country where GNP >(...) and Continent='Asia';
)
第三步:city 表需要和临时表依据国家代码进行内连接,即在第二步还需要国家代码字段(code
),因此有:
select
a.name as 城市名
,
b.name as 国家名
from
city as a
inner join (
select
name,
code
from
country
where
GNP > (
select
GNP
from
country
where
name = 'Aruba'
)
and Continent = 'Asia'
) as b on a.countrycode = b.code;
输出为 1763 条记录:
城市名 国家名
Kabul Afghanistan
Qandahar Afghanistan
Herat Afghanistan
Mazar-e-Sharif Afghanistan
Dubai United Arab Emirates
Abu Dhabi United Arab Emirates
Sharja United Arab Emirates
al-Ayn United Arab Emirates
Ajman United Arab Emirates
...
exists 关键字
该关键字用来检测查询出来的 SQL 数据是否至少返回一行数据,返回的结果类似为布尔类型—— 0 (false)或 1 (true)
来看这样的一个简单例子:
# 输出的结果为 0
select exists(select * from country where gnp="800");
例如,查询有独立时间的国家的城市:
# 在没有 exists 关键字之前
## 输出为 4018 条记录
select name from city where CountryCode in (select code from country where IndepYear is not null);
# 如果使用 exists 关键字
## 输出为 4018 条记录
select name from city where exists (
select * from country where city.countrycode=country.code and indepyear is not null
);
在第一种 SQL 语句中,由于有 in 关键字,其执行的先后顺序比较好理解:
select name from city where CountryCode in (select code from country where IndepYear is not null);
|<-- 1 -->|
|<-- 2 -->|
|<-- 3 -->|
|<-- 4 -->|
在第二种 SQL 中,由于有 exists 关键字,其执行顺序则比较迷糊:
select name from city where exists (select * from country where city.countrycode=country.code and indepyear is not null);
|<-- 1 -->|
|<-- 2 -->|
|<-- 3 -->|
其先定位在 city 表,然后将 city 表和 country 表的数据通过条件(city.countrycode=country.code and indepyear is not null
)一行一行匹配,如果是 true,返回结果;如果是 false,不返回结果,显示的结果集以 name
字段进行列出。
另外请注意!由于 exists
返回的是布尔类型,因此上面的 *
可以替换为任意的查询列表。
通常,我们都建议您首先使用 in
关键字,但在一些数据比较多的查询中,exists
的效率要比 in
高。
分页查询
需要使用到 limit
关键字,其完整的语法为:
select 查询列表 from 表1 [join type] join 表2 on 连接条件
where 筛选条件
group by 分组字段 having 分组筛选
order by 排序字段
limit 起始索引,要显示的条目数;
当您使用 limit
关键字后,其初始索引从 0 开始记。
请不要将结果集的初始索引和字符串的初始索引混淆:
- 结果集索引从 0 开始记
- 字符串索引从 1 开始记,例如前面提到的 substr(str,x,n) 函数,表示从字符串的 x 索引位置截取长度为 n 的子字符串。
来看这样的一个例子:
# 查询有独立时间的国家所对应的城市名称,并将这些城市按照降序排列,仅显示前 20 条记录
select
a.name
from
city a
where
a.countrycode in (
select
code
from
country
where
indepyear is not null
)
order by
a.name desc
limit
0, 20;
它的执行顺序为:
( )
里是最先执行的部分- 定位到 city 表
- 执行筛选条件(
where a.countrycode in
) - 以
a.name
城市名称呈现结果集 - 对结果集按照城市名称降序排列(
order by a.name desc
) - 仅显示前 20 条记录(
limit 0,20
)
联合查询
需要使用到 union
关键字
联合查询:指将两次或多次查询出来的结果集合并成为一张表(或一个结果集),前提条件是要求多次查询的列数以及数据类型是一致的。联合查询使用到的关键字为 union
,可在一条 SQL 语句中使用多次。
前面提到了内连接和外连接,它指的是两张表都有一定的关联性(例如 city 表的 countrycode 字段和 country 表的 code 字段能进行关联)。现在的问题是,如果两张表或多张表没有关联性呢?这时候可以考虑使用联合查询
例如,
# 查询国家名称中以 ch 开头或 GNP 等于 828 的国家信息:
## 结果集为 5 条记录
select * from country where name like 'ch%' or gnp=828;
# 可以在 5 条记录的基础上再追加一条记录
select * from country where name like 'ch%' or gnp=828
union
select * from country where code='usa';
Q:两次或多次查询的列数一致,是否允许它们的数据类型不一致?
是的。如下:
select code,name from country where name like 'ch%'
union
select gnp,code from country where gnp=828;
输出为:
code name
CHL Chile
CHN China
CXR Christmas Island
TCD Chad
828.00 ABW
Q:使用 union
关键字时,若有重复的数据,最后的结果集是去重还是不去重?
去重。
select code,name from country where name like 'an%'
union
select code,name from country where gnp=6648;
输出为:
code name
AGO Angola
AIA Anguilla
AND Andorra
ATA Antarctica
ATG Antigua and Barbuda
若您需要重复的数据,可以使用关键字 union all
select code,name from country where name like 'an%'
union all
select code,name from country where gnp=6648;
输出为:
code name
AGO Angola
AIA Anguilla
AND Andorra
ATA Antarctica
ATG Antigua and Barbuda
AGO Angola
