MySQL基础10—DQL4

概述

本章,您将学习 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 ... );

如您所见,我们可以把单行子查询的结果集作为条件判断的值来对待。

列子查询

可以使用 inanyall 等关键字:

  • 当用 in 关键字时,表示等于子查询的任意一个,即多个 or 的条件查询
  • 当用 any 关键字时,表示和子查询的任意一个值做比较,例如 select .... from ... where id>any(select ...);
  • 当用 all 关键字时,表示和子查询的所有值做比较,即多个 and 的条件查询。

select 语法中,anyall 关键字都必须搭配条件表达式进行使用。条件表达式即:

  • > – 表示大于
  • < – 表示小于
  • >= – 表示大于等于
  • <= – 表示小于等于
  • <> – 表示不等于
  • <=> – 表示安全等于,不太常用
  • = – 表示等于

三者的对比:

关键字 比较方式 逻辑关系 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;

它的执行顺序为:

  1. ( ) 里是最先执行的部分
  2. 定位到 city 表
  3. 执行筛选条件(where a.countrycode in
  4. a.name 城市名称呈现结果集
  5. 对结果集按照城市名称降序排列(order by a.name desc
  6. 仅显示前 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
Avatar photo

关于 陸風睿

GNU/Linux 从业者、开源爱好者、技术钻研者,撰写文档既是兴趣也是工作内容之一。Q - "281957576";WeChat - "jiulongxiaotianci",Github - https://github.com/jimcat8
用一杯咖啡支持我们,我们的每一篇[文档]都经过实际操作和精心打磨,而不是简单地从网上复制粘贴。期间投入了大量心血,只为能够真正帮助到您。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇