​在数据库应用中大部分使用的是查询,在数据库系统中,一个查询执行的过程如图17-36所示。

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_连接查询

图17-36数据查询过程​

从图17-36可以看出:一个查询的执行过程是由客户端向数据库服务器发出查询请求,数据库服务器接收到客户端的请求后对请求进行处理,在处理客户端发出的条件后,将查询到的结果以集合的形式返回给客户端,完成整个的查询过程。专业上把数据库服务器查询到的结果称为“查询结果集”,简称“结果集”。​

为方便读者学习查询语句的使用,需要读者先建立两张数据库表,这两张表的名称分别是games和scores,games表的结构如图17-37所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_数据查询_02

图17-37 games表的结构​

scores表的结构如图17-38所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_SQL_03

图17-38scores表的结构​

games、scores以及之前建立过的users三张表共同构成了一个记录QQ游戏分数的小系统。users表记录的是游戏玩家的信息,它的各个字段意义如表17-4所示。​

表17-4 users表各字段意义​

字段

意义

USER_QQ

玩家QQ号

USER_NAME

玩家昵称

USER_SEX

玩家性别

USER_BIRTHDAY

玩家生日

USER_PHONE

玩家手机号

games表各个字段的意义如表17-5所示。​

表17-5 games表各字段意义​

字段

意义

GNO

游戏编号

GNAME

游戏名称

GTYPE

游戏类型

scores表各个字段的意义如表17-6所示。​

表17-6 scores表各字段意义​

字段

意义

USER_QQ

游戏玩家QQ号

GNO

玩家所玩游戏的编号

SCORE

玩家完某一游戏取得的分数

为使读者能够更好的观察查询结果,还需向表中插入一些基础数据,读者可复制以下插入语句并执行它们。​

data.sql​

INSERT INTO users VALUES ('12301', '小叮当', '女', '1995-03-06', '13900000000');
INSERT INTO users VALUES ('12302', '孙小美', '女', '1996-08-25', '15911112222');
INSERT INTO users VALUES ('12303', '钱多多', '男', '1995-09-15', '13699990000');
INSERT INTO users VALUES ('12304', '忍者', '男', '1993-04-02', '18866669999');
INSERT INTO users VALUES ('12305', '孙悟空', '男', null, '18666688888');
INSERT INTO users VALUES ('12306', '阿拉蕾', '女', '1992-12-06', '13599997777');

INSERT INTO games VALUES (1, '俄罗斯方块', '益智');
INSERT INTO games VALUES (2, '推箱子', '益智');
INSERT INTO games VALUES (3, '四人麻将', '棋牌');
INSERT INTO games VALUES (4, '斗地主', '棋牌');
INSERT INTO games VALUES (5, '桌球', '体育');
INSERT INTO games VALUES (6, '神奇赛车', '体育');

INSERT INTO scores VALUES ('12301', 1, 2420);
INSERT INTO scores VALUES ('12301', 2, 4520);
INSERT INTO scores VALUES ('12301', 4, 7820);
INSERT INTO scores VALUES ('12302', 3, 3620);
INSERT INTO scores VALUES ('12302', 5, 7820);
INSERT INTO scores VALUES ('12303', 3, 4220);
INSERT INTO scores VALUES ('12303', 6, 6220);
INSERT INTO scores VALUES ('12304', 2, 1520);

做完以上准备工作后,就可以开始学习查询语句了。​

17.6.1基本数据查询


查询语句的格式为:​

SELECT COL1, COL2,…COLn

FROM TABLE1,TABLE2…TABLEn

[ WHERE CONDITIONS ]

[ GROUP BY GROUP_BY_LIST ]

[ HAVING CONDITIONS ]

[ ORDER BY ORDER_LIST [ ASC | DESC ] ]

在这个格式中,“SELECT”是一个关键字,表示查询,“COL1, COL2,…COLn”是列的名称,“SELECT COL1, COL2,…COLn”表示查询COL1, COL2,…COLn这些列的数据,“FROM”也是关键字,表示数据从哪个或哪些表中来。“FROM TABLE1,TABLE2…TABLEn”表示数据来源于TABLE1,TABLE2…TABLEn这些表。​

查询语句的后半部分均是可选子句,这些子句的意义分别是:​

  • WHERE CONDITIONS:表示查询条件​
  • GROUP BY GROUP_BY_LIST:表示分组条件​
  • HAVING CONDITIONS:是统计结果条件​
  • ORDER BY ORDER_LIST [ ASC | DESC ]:排序字句​

读者暂时可以不同理解那些可选子句,这些子句的作用和意义将在本节后面的部分详细讲解。​

下面以例题的形式详细讲解查询语句的用法。​

【例17_08查询Users表中全部列的数据】

Exam17_08.sql​

SELECT USER_QQ, USER_NAME, USER_SEX, USER_BIRTHDAY, USER_PHONE​
FROM USERS​

题目要求查询Users表中全部列的数据,因此SELECT关键字后面写上了Users表的全部列的名称,需要注意:SELECT关键字后面列名出现的顺序必须可以与Users表中实际列的顺序不一致,结果集中每一列数据的顺序以SELECT关键字后面列名出现的顺序为准。实际上,SQL语句中使用“*”可以代表全部列,因此【例17_08】还可以用以下语句完成查询。​

SELECT * FROM USERS​

【例17_08】的运行结果如图17-39所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_联合查询_04

图17-39【例17_08】运行结果​

如果希望只查询一张表中部分列中的数据,可以在SELECT关键字后只列出希望查询列的名称。下面的【例17_09】是只查询玩家QQ号和昵称的语句。​

【例17_09 只查询玩家QQ号和昵称】

Exam17_09.sql​

SELECT USER_QQ,USER_NAME FROM USERS​

如果希望结果集中列的名称显示为程序员指定的名称,可以使用AS关键字为列指定别名,只需要在真实列名称后面用AS连接一个别名就可以,列名称、AS关键字以及别名之间以空格隔开,实际上AS关键字甚至可以不出现。下面的【例17_10】是指定别名的SQL语句。​

【例17_10指定别名】

Exam17_10.sql​

SELECT USER_QQ AS '玩家QQ',USER_NAME AS '玩家昵称' FROM USERS​

如果省略AS关键字,以上语句可以写为:​

SELECT USER_QQ   '玩家QQ',USER_NAME '玩家昵称' FROM USERS​

【例17_10】的运行结果如图17-40所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_联合查询_05

图17-40【例17_10】运行结果​

有时候需要把查询结果集中完全重复的行显示为一行,例如以下语句:​

SELECT USER_QQ FROM SCORES​

这条语句的执行结果如图17-41所示。

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_数据查询_06

图17-41结果集中出现完全重复的行​

从图17-41可以看出:某些查询语句产生的结果集中会出现完全重复的行,SQL语句中的DISTINCT关键字能够消除完全重复的行,下面的【例17_11】展示了如何使用DISTINCT关键字消除查询结果集中的重复行。​

【例17_11使用DISTINCT关键字消除结果集中的重复行】

Exam17_11.sql​

SELECT DISTINCT USER_QQ FROM SCORES​

【例17_11】的运行结果如图17-42所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_SQL_07

图17-42【例17_11】运行结果​

从图17-42可以很明显的看出:结果集中的重复行被消除掉了,完全一样的两行结果只出现一次。​

使用SELECT关键字查询数据时都会把所有满足条件的数据全部显示出来,如果想只显示部分数据,可以用LIMIT关键字实现,LIMIT关键字要出现在查询语句的末尾,它两种用法,第一种用法是在LIMIT关键字之后加一个数字N,表示只显从第一条到第N条的数据,下面的【例17_12】展示了LIMIT关键字的第一种用法。​

【例17_12 LIMIT关键字1】

Exam17_12.sql​

SELECT * FROM USERS LIMIT 3

【例17_12】的运行结果如图17-43所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_连接查询_08

图17-43【例17_12】运行结果​

LIMIT关键字还有第二种用法,那就是在LIMIT关键字之后加两个数字M和N,它们之间以逗号隔开,这样写表示显示从第M条之后的数据开始显示,最多总共显示N条数据,下面的【例17_13】展示了LIMIT关键字的第二种用法。​

【例17_13 LIMIT关键字2】

Exam17_13.sql​

SELECT * FROM USERS LIMIT 2,3

【例17_13】的运行结果如图17-44所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_数据查询_09

图17-44【例17_13】运行结果​

17.6.2指定条件的查询

17.6.2小节所介绍的查询都是不带有条件的查询,实际开发过程中往往会只查询那些满足条件的数据,例如查询性别为女的玩家信息,其中“性别为女”就是一个查询条件。在这种情况下就要使用带有条件的查询语句,为查询语句添加条件要用到WHERE关键字,在WHERE关键字的后面添加一个或多个条件表达式,这样的查询语句在执行后就能够查询出那些满足条件的数据,而不满足条件的数据则不会出现在结果集中。下面的【例17_14】展示了一个带有条件的查询语句。​

【例17_14 查询QQ号为12301的玩家信息】

Exam17_14.sql​

SELECT * FROM USERS WHERE USER_QQ='12301'

【例17_14】的运行结果如图17-45所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_数据查询_10

图17-45【例17_14】运行结果​

【例17_14】的SQL语句中,使用“=”设置条件,“=”表示等于,SQL的其他比较运算符如表17-7所示。​

表17-7 SQL的比较运算符​

比较运算符

意义

=

等于

<>

不等于

>

大于

>=

大于等于

<

小于

<=

小于等于

如果有多个条件,可以用逻辑运算符进行连接,表17-8展示了SQL的逻辑运算符。​

表17-8 SQL的逻辑运算符​

运算符

意义

AND

并且

OR

或者

NOT

下面的【例17_15】展示了如何使用条件运算符设定两个条件。​

【例17_15 查询游戏分数大于等于3500到小于等6000的分数信息】

Exam17_15.sql​

SELECT * FROM SCORES WHERE SCORE>=3500 AND SCORE<=6000

【例17_15】的运行结果如图17-46所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_SQL_11

图17-46【例17_15】的运行结果​

【例17_15】查询了一个范围内的数值,实际上,SQL还可以用BETWEEN...AND关键字来设定一个查询范围,使用BETWEEN...AND关键字时,BETWEEN的后面写较小的数值,AND的后面写较大的数值,这样的关键字组成的范围包含了两个边界,也就是说:如果语句中出现了“BETWEEN 100 AND 200”,实际上等同于“大于等于100小于等于200”。下面的【例17_16】使用BETWEEN...AND查询一个范围内的分数信息。​

【例17_16 用BETWEEN...AND查询游戏分数大于等于3500到小于等6000的分数信息】

Exam17_16.sql​

SELECT * FROM SCORES WHERE SCORE BETWEEN 3500 AND 6000

【例17_16】的运行结果与【例17_15】的运行结果完全相同,读者可以自行。BETWEEN...AND除了可以设定数值的范围外,还可以设定日期时间的范围,请看下面的【例17_17】​

【例17_17 查询1993年1月1日到1995年12月31日出生的玩家】

Exam17_17.sql​

SELECT * FROM USERS ​
WHERE USER_BIRTHDAY BETWEEN '1993-01-01' AND '1995-12-31'

【例17_17】的运行结果如图17-47所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_MySQL_12

图17-47【例17_17】的运行结果​

有时候给查询语句设定的条件并非是一个特定的值,而是符合某种特征的结构,在这种情况下可以使用通配符。SQL常用的通配符有“_”和“%”,其中“_”表示任意一个字符,而“%”表示任意N个字符(包括0个字符),当使用通配符时,需要用LIKE关键字,如果使用“=”则通配符会失去效果。下面的【例17_18】展示使用通配符查询所有姓孙的玩家信息。​

【例17_18查询所有姓孙的玩家信息】

Exam17_18.sql​

SELECT * FROM USERS WHERE USER_NAME LIKE '孙%'

【例17_18】的运行结果如图17-48所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_数据查询_13

图17-48【例17_18】运行结果​

从图17-48可以看出:玩家孙悟空的生日为空(NULL),如果希望查询某列值为空的数据,要用IS NULL关键字实现,相反的,如果查询某列值不为空的数据,则用IS NOT NULL,请看下面的【例17_19】​

【例17_19 查询生日为NULL的玩家信息】

Exam17_19.sql​

SELECT * FROM USERS WHERE USER_BIRTHDAY IS NULL

【例17_19】的运行结果如图17-49所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_MySQL_14

图17-49【例17_19】的运行结果​

17.6.3对查询结果排序

每次执行查询语句后,查询结果都是按自然顺序排列的。实际上SQL允许程序员按照自己的规则对查询结果进行排序。对查询结果进行排序时,需要指出依据哪一个列上的数据进行排序,例如对于Users表中的玩家信息,可以按照生日进行排序,也可以按照手机号进行排序。程序员不仅仅要指出排序的依据,还要指出排序的方式。排序方式分两种,升序和降序。升序排序是指小的数值在前,大的数值在后,而降序方式则相反,大的数值在前,小的数值在后。​

SQL完成查询结果集排序的语法格式是:​

SELECT COL_LIST FROM TABLE_NAME

ORDER BY ORDER_BY_LIST [ASC|DESC]

在这个格式中,ORDER BY是排序的关键字,表示以哪些列为依据进行排序,而ORDER_BY_LIST表示参与排序的列的名称。SQL允许同时一句多个列进行排序,如果以多个列为依据进行排序,先出现的列优先级更高,后出现的列优先级较低。列名称后面的“ASC”或“DESC”也是关键字,“ASC”表示升序排列方式,“DESC”表示降序排列方式。如果程序员没有在语句中指定排序方式,则默认按升序方式排列数据。下面的【例17_20】是只以一个列为依据进行排序的查询语句。​

【例17_20查询分数表中所有分数信息并按照分数升序排序】

Exam17_20.sql​

SELECT * FROM SCORES ORDER BY SCORE ASC

【例17_20】的运行结果如图17-50所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_联合查询_15

图17-50【例17_20】运行结果​

如果同时以多个列为依据对查询结果进行排序,则在ORDER BY关键字后面写多个列的名称,列名称之间以逗号(,)隔开,先书写的列排序优先级高,每个列都可以指定自己的排序方式。下面的【例17_21】是同时以多个列为依据进行排序的查询语句。​

【例17_21查询分数表所有信息,并按照游戏编号的升序和分数的降序进行排序】

Exam17_21.sql​

SELECT * FROM SCORES ORDER BY GNO ASC, SCORE DESC

【例17_21】的运行结果如图17-51所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_数据查询_16

图17-51【例17_21】运行结果​

17.6.4聚合函数

聚合函数也称组函数,它们是用来对一列数据进行统计以及计算的函数,MySQL的聚合函数如表17-9所示。​

表17-9 MySQL的聚合函数​

聚合函数

功能

支持的数据类型

sum()

对指定列中的所有非空值求总和

avg()

对指定列中的所有非空值求平均值

min()

返回指定列中的最小数字、最早的日期或者最小的字符串

、字符日期时间

max()

返回指定列中的最大数字、最近的日期或者最大的字符串

、字符日期时间

count()

统计结果集合中全部记录行的数量

任意数据类型

下面的用一些例子演示各个聚合函数的作用。​

【例17_22查询玩家表中一共有多少名玩家信息】

Exam17_22.sql​

SELECT COUNT(USER_QQ) FROM USERS​

【例17_22】的运行结果如图17-52所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_SQL_17

图17-52【例17_22】运行结果​

【例17_22】的SQL语句中,COUNT()函数的参数是USER_QQ列,这个列不允许出现空值,如果一个列上允许出现空值,那么以这个列的名称为COUNT()函数的参数时,空值将不被统计在内,为了避免这种情况出现,可以用“*”作为COUNT()函数的参数,这样只要一个行上存在不为空的值,这一行就被统计在内,因此【例17_22】的SQL语句也可以写为:​

SELECT COUNT(*) FROM USERS​

程序员也可以给聚合函数的进行计算结果设置一个别名,设置别名的方式与给普通列设置别名的方式相同,下面的【例17_22】展示了如何为聚合函数的计算结果设置别名。​

【例17_23查询玩家表中一共有多少名玩家信息并把计算结果设为总人数】

Exam17_23.sql​

SELECT COUNT(*) AS '总人数' FROM USERS​

【例17_23】的运行结果如图17-53所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_MySQL_18

图17-53【例17_23】运行结果​

【例17_24查询QQ号是12301的玩家游戏的总分数】

Exam17_24.sql​

SELECT SUM(SCORE) AS '总分数' FROM SCORES WHERE USER_QQ='12301'

【例17_24】的运行结果如图17-54所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_数据查询_19

图17-54【例17_24】的运行结果​

【例17_25查询QQ号是12302的玩家的平均分数】

Exam17_25.sql​

SELECT AVG(SCORE) AS '平均分数' FROM SCORES WHERE USER_QQ='12302'

【例17_25】的运行结果如图17-55所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_数据查询_20

图17-55【例17_25】运行结果​

【例17_26查询QQ号是12301玩家的最高分和最低分】

Exam17_26.sql​

SELECT MAX(SCORE) AS '最高分',MIN(SCORE) AS '最低分'
FROM SCORES WHERE USER_QQ='12301'

【例17_26】的运行结果如图17-56所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_MySQL_21

图17-56【例17_26】的运行结果​

MAX()和MIN()这两个聚合函数还可以用于求最晚的日期时间和最早的日期时间。需要注意,SQL语句在做运算时,认为先出现的日期时间小,后出现的日期时间大,下面的【例17_27】展示了使用MAX()和MIN()对日期时间的统计效果。​

【例17_27 查询出生最早的玩家生日和出生最晚的玩家生日】

Exam17_27.sql​

SELECT MIN(USER_BIRTHDAY) AS '最早生日', ​MAX(USER_BIRTHDAY) AS '最晚生日'
FROM USERS​

【例17_27】的运行结果如图17-57所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_数据查询_22

图17-57【例17_27】运行结果​

以上几个例子都是对整个数据表中的某列数据进行统计,实际上,聚合函数还可以完成“分组统计”的操作。所谓“分组统计”就是先按照一定依据对数据进行分组,然后在组内对数据进行统计,这样就能达到统计的精细化。例如全校有1000个学生,如果对所有学生求平均身高,这样的统计针对性不强。如果先按性别把学生分成男女两组,之后再分别统计男生的平均身高和女生的平均身高,这样的统计结果具有更强的针对性。对数据进行分组的关键字是GROUP BY,GROUP BY后面是作为分组依据的列的名称。下面的【例17_28】展示了分组统计的效果。​

【例17_28 查询每个玩家的总分数、平均分数、最高分数并显示玩家的QQ号】

Exam17_28.sql​

SELECT USER_QQ,SUM(SCORE) AS '总分',
AVG(SCORE) AS '平均分',MAX(SCORE) AS '最高分'
FROM SCORES GROUP BY USER_QQ​

【例17_28】的运行结果如图17-58所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_连接查询_23

图17-58【例17_28】运行结果​

从图17-58可以看出:scores表中user_qq相同的数据被分为一组,并且对每一组分数进行求总分、平均分和最高分的统计。​

实际上,SQL还可以用聚合函数的计算结果作为查询条件,但与用普通数据做查询条件不同的是,以聚合函数作为统计结果时需要用HAVING作为设置查询条件的关键字,并且如果SQL语句中对数据进行了分组的情况下,HAVING字句要写在GROUP BY字句的后面。下面的两个例子展示了如何使用聚合函数的计算结果作为查询条件。​

【例17_29 查询平均分数大于4000的玩家QQ号、总分数、平均分数】

Exam17_29.sql​

SELECT USER_QQ, SUM(SCORE) AS '总分数',AVG(SCORE) AS '平均分数'
FROM SCORES GROUP BY USER_QQ HAVING AVG(SCORE)>4000

【例17_29】的运行结果如图17-59所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_SQL_24

图17-59【例17_29】运行结果​

SQL语句中即使出现了HAVING字句也仍然能进行排序操作,下面的【例17_30】展示了对含有HAVING字句的查询结果排序的SQL语句。​

【例17_30 查询平均分数大于4000的玩家QQ号、总分数、平均分数并按平均分数倒序排列】

Exam17_30.sql​

SELECT USER_QQ, SUM(SCORE) AS '总分数',AVG(SCORE) AS '平均分数'
FROM SCORES GROUP BY USER_QQ HAVING AVG(SCORE)>4000
ORDER BY AVG(SCORE) DESC

【例17_30】的运行结果如图17-60所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_MySQL_25

图17-60【例17_30】运行结果​

17.6.5连接查询

在实际开发过程中,往往不仅只从一张表中查询数据,很多时候是从多张表中共同查询数据,例如,在查询分数表时,如果只查询Scores表只能显示出玩家的QQ号而不能显示出玩家的昵称,同理,也只能显示出游戏的编号而不能显示出游戏的名称。如果希望能够把玩家QQ号、游戏名称以及玩游戏所得的分数显示在同一个结果集中,就必须使用连接查询。​

连接查询分为内连接和外连接,内连接又可以分为显式内连接和隐式内连接,而外连接又可以分为左外连接和右外连接。本小节将详细讲解这几种连接查询。​

在一个SELECT语句中,FROM关键字之后可以出现多张数据表的名称,如果这个SELECT语句没有WHERE字句(即没有任何查询条件),那么这个SELECT语句的查询结果是多张表数据的完全排列组合,这种完全的排列组合会出现很多“张冠李戴”的情况,例如在不加任何条件的情况下让表示玩家的Users表和表示分数的Scores表同时出现在FROM关键字后,查询出的数据会出现张三的昵称连接到李四的分数上这样的错误情况,因此必须设置一个条件让两张表或多张表中的数据能够正确的连接起来。多张数据表中的数据通常都以某个相同的字段值作为归属关系的判断依据,例如:Users表中张三的QQ号是12301,那么在Scores表中出现的数据如果其USER_QQ值也是12301,那么这条数据中的分数(SCORE值)就一定是属于张三的。所以避免出现数据之间张冠李戴情况出现的办法就是在书写SELECT语句时规定:只有在Users表的USER_QQ字段值与Scores表的USER_QQ字段值相同的情况下才满足条件。下面的【例17_31】展示了如何查询玩家昵称、游戏名称以及游戏分数,并让它们正确的显示在同一个结果集中。​

【例17_31查询分数信息,显示玩家昵称、游戏名称和分数】

Exam17_31.sql​

SELECT USER_NAME AS '昵称',GNAME AS '游戏名称',SCORE AS '分数'
FROM USERS,GAMES,SCORES​
WHERE USERS.USER_QQ=SCORES.USER_QQ
AND GAMES.GNO=SCORES.GNO

【例17_31】的运行结果如图17-61所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_连接查询_26

图17-61【例17_31】运行结果​

从图17-61可以看出:分属于三张表的USER_NAME、GNAME和SCORE字段正确的出现到了同一个结果集中。​

【例17_31】的SQL语句是一种隐式内连接的写法,隐式内连接在书写查询语句时,在FROM关键字的后面直接写多张表的名称,并以逗号相隔。实际上SQL还提供了一种显式内连接的写法,所谓显式内连接就是在语句中能够直接体现出两张表具有内连接关系,其格式为:​

SELECT COL_LIST

FROM TABLE1 [INNER] JOIN TABLE2

ON TABLE1.COL=TABLE2.COL

在这个格式中,FROM关键字的后面并不会连续出现两张表的名称,而是以“INNER JOIN”关键字表明两张表有内连接关系,而设置连接条件的关键字变成了“ON”。下面的【例17_32】展示了如何使用显示内连接的格式完成与【例17_31】相同的查询效果。​

【例17_32以显示内连接形式查询分数信息,显示玩家昵称、游戏名称和分数】

Exam17_32.sql​

SELECT USER_NAME AS '昵称', GNAME AS '游戏名称', SCORE AS '分数'
FROM GAMES INNER JOIN SCORES ON GAMES.GNO=SCORES.GNO
INNER JOIN USERS ON SCORES.USER_QQ=USERS.USER_QQ

【例17_32】的运行结果如图17-62所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_连接查询_27


图17-62【例17_32】运行结果​

从图17-61和17-62可以很明显的看出:隐式内连接和显式内连接具有同样的查询效果。从以上两个例子可以总结出内连接的特点:相连接的两张表地位平等,如果一张表中在另一张表中找不到对应数据时则不做连接,例如Users表中有QQ号为12305(孙悟空)和12306(阿拉蕾)的玩家,但Scores表中并不存在USER_QQ值为12305和12306的数据,因此查询结果集中也没有出现QQ号为12305(孙悟空)和12306(阿拉蕾)的数据。之所以Scores表中不存在USER_QQ值为12305和12306的数据,是因为他们只是注册了游戏用户但没有参与游戏,因此Scores表中没有其分数信息。​

与内连接相对的是外连接,外连接时做连接的两个表地位不平等,其中有一张是基础表,基础表中的每条数据必须出现,即使另一张表中没有数据与之匹配,也要用NULL补齐。外连接又分为左外连接和右外连接,左外连接时左表是基础表,右外连接时右表是基础表。如何区分左表还是右表呢?SELECT语句中先出现的表为“左表”,后出现的表为“右表”。外连接的格式为:​

SELECT COL_LIST

FROM TABLE1 LEFT | RIGHT [OUTER] JOIN TABLE2

ON TABLE1.COL=TABLE2.COL

在外连接的格式中出现了“|”,它表示排他性选择,也就是说在外连接语句中“LEFT”和“RIGHT”只能选择其一,不能同时出现。下面的【例17_33】展示了外连接的查询效果。​

【例17_33 查询所有玩家关于5号游戏的分数信息】

Exam17_33.sql​

SELECT USER_NAME AS '昵称',GNO AS '游戏编号',SCORE AS '分数'
FROM USERS LEFT JOIN SCORES ​
ON USERS.USER_QQ=SCORES.USER_QQ AND SCORES.GNO=5

【例17_33】的SQL语句把Users表和Scores表进行了左外连接,可以看出:语句中先出现了Users表,因此Users表是左表,而左外连接左表是基础表,查询结果集中左表的数据必然出现,【例17_33】的运行结果如图17-63所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_SQL_28

图17-63【例17_33】运行结果​

从图17-63中可以看出:左表(Users)中的数据全部出现,这满足了题目查询“所有”玩家5号游戏的要求,即使某些玩家没有玩过5号游戏,结果集中也以NULL补齐了结果集。​

17.6.6子查询

子查询是嵌入到一个外部查询中的SELECT语句,子查询的结果集可以作为外部查询的条件或表名称。子查询可以分为两种:简单子查询和相关子查询。简单子查询是与外部查询无关的子查询,它可以独立运行。在查询是,简单子查询只执行一次,并将结果传递给外部查询。相关子查询至少会引用外部查询中的一个列,所以它不能够自己独立运行。在查询中,必须首先运行外部查询,然后为外部查询中的每一个行执行一次相关子查询。​

首先讲解简单子查询。简单子查询的执行步骤是:​

  1. 执行一次简单子查询。​
  2. 将结果传递给外部查询。​
  3. 执行一次外部查询。​

大多数基本的简单子查询会返回单个值,在外部查询中可以把它当作一个表达式来使用。一般情况下,简单子查询是用IN关键字引出的。下面的【例17_34】展示了使用子查询设置条件的例子。​

【例17_34 查询游戏类型是棋牌类的游戏的分数信息】

Exam17_34.sql​

SELECT * FROM SCORES​
WHERE GNO IN
(SELECT GNO FROM GAMES WHERE GTYPE='棋牌')

从以上SQL语句中可以看出:作为IN关键字后面的小括号中又有一个SELECT语句,这个SELECT语句就是子查询,它负责找打所有棋牌类游戏的编号,这些编号可能有多个,从而形成一个范围,而IN关键字就表示在这个范围内,而外围的SELECT语句是查询分数信息,所查询的分数信息必须满足其游戏编号在子查询所查到的范围内的条件。​

【例17_34】的运行结果如图17-64所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_SQL_29

图17-64【例17_34】运行结果​

下面讲解相关子查询。相关子查询的使用方法与普通子查询一样,它们的区别在于:相关子查询引用了外部查询中的列。这种用外部查询来限制子查询的方法使得SQL查询变得更加强大和灵活。因为相关子查询能够引用外部查询,所以它们尤其适合编写复杂的where条件。这种引用外部查询的能力也意味着相关子查询不能够自己单独运行,其中对于外部查询的引用会使其无法正常执行。相关子查询的逻辑执行顺序如下:​

  1. 首先执行一次外部查询。​
  2. 对于外部查询中的每一行分别执行一次子查询,而且每次执行子查询时都会引用外部查询中当前行的值。​
  3. 使用于查询的结果来确定外部查询的结果集。​

一般情况下,简单子查询是用EXISTS关键字引出的。下面的【例17_35】展示了相关子查询的使用方法。​

【例17_35查询没有玩5号游戏的玩家信息】

Exam17_35.sql​

SELECT * FROM USERS WHERE NOT EXISTS​
(SELECT * FROM SCORES ​
WHERE USERS.USER_QQ=SCORES.USER_QQ AND SCORES.GNO=5)

【例17_35】的运行结果如图17-65所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_MySQL_30

图17-65【例17_35】运行结果​

17.6.7联合查询

联合查询又称为并操作,按照关系代数的术语来说,联合是加法,而连接是乘法。与连接不同,联合将多个结果集“纵向”集中到同一个结果集中。​

在SQL中,UNION运算符可以将两个或两个以上SELECT语句的查询结果集合合并成一个结果集合,即执行联合查询。UNION运算符的语法格式为:​

SELECT_STATEMENT

UNION [ALL] SELECT_STATEMENT

[UNION [ALL] SELECT_STATEMENT] [,...n]

žSELECT_STATEMENT为待联合的SELECT查询语句。ALL选项说明将所有行合并到结果集合中。不指定该选项时,被联合查询结果集合中的重复行将只被保留一行。使用联合查询时必须遵循以下几条规则:​

  • 应保证每个被联合的SELECT语句的选择列表具有相同的结构:有相同的列数,并且结果集合中对应列的数据类型必须兼容。​
  • 只有第一个SELECT语句中可以使用INTO子句,它将最终结果集合插入表中。​
  • 只有在UNION语句的结尾才允许使用ORDER BY子句。​
  • GROUP BY 和HAVING子句只能在单独的查询中使用;它们不影响最终结果集合。​

下面的【例17_36】展示了联合查询的执行效果。​

【例17_36 查询QQ号是12301的玩家所有分数并计算总分和平均分数据显示到同一结果集中】

Exam17_36.sql​

SELECT USER_QQ,GNO,SCORE FROM SCORES WHERE USER_QQ='12301'
UNION ALL​
SELECT '总分', ' ', SUM(SCORE) FROM SCORES WHERE USER_QQ='12301'
UNION ALL​
SELECT '平均分',' ' ,AVG(SCORE) FROM SCORES WHERE USER_QQ='12301'

【例17_36】的运行结果如图17-66所示。​

第十七章《MySQL数据库及SQL语言简介》第6节:数据查询_连接查询_31

图17-66【例17_36】运行结果​

从图17-66可以看出:几条查询语句的结果集被纵向组合到了一起。​

本文字版教程还配有更详细的视频讲解,小伙伴们可以点击这里观看。