合并查询

在前文我们介绍了如何使用子查询和连接查询在多个表中查询数据,本节我们将介绍另一种在多表中查询数据的方法:合并查询。

合并查询顾名思义就是将多个查询结果合并在一起,比如我们现在有如下两张表:学生表和老师表,我们想要查询学校中的所有人员信息(包括老师和学生),这时候我们就可以使用合并查询。

学生表(表名为students):

id name telephone gender class
1 屈屈 18820030216 1班
2 慧心 13268751479 2班
3 达琳 15754874518 1班
4 森贝 13698742277 2班
5 米粒 13698652689 1班

老师表(表名为teachers):

id name telephone gender
1 困困 13026324789
2 毛毛 13658748974
3 米粒 13265877158

UNION

我们想要查询学校中所有人员的姓名和电话号码,此时可以使用如下SQL语句:

1
2
3
SELECT name, telephone FROM students
UNION
SELECT name, telephone FROM teachers;

这里的UNION就表示取两个查询结果的并集,即将两个查询结果合并在一起。
其中SELECT name, telephone FROM students为第一个查询,SELECT name, telephone FROM teachers为第二个查询,将两个查询结果合并在一起,我们就得到了学校中所有人员的姓名和电话号码:

name telephone
屈屈 18820030216
慧心 13268751479
达琳 15754874518
森贝 13698742277
米粒 13698652689
困困 13026324789
毛毛 13658748974
米粒 13265877158

也就是说UNION会将两个查询结果合并在一起,那如果两个查询的列不一样会怎么样呢?我们来看一个例子:

1
2
3
SELECT name, telephone FROM students
UNION
SELECT telephone, name FROM teachers;

第一个查询的列是nametelephone,而第二个查询的列是telephonename,这两个查询的列不一样,那么UNION会如何处理呢?

将会返回如下结果,可以看到UNION会将两个查询结果合并在一起,不管两个查询的列是否一样,使用第一个查询的列作为最终结果的列。这样的查询实际并没有意义,在使用时应注意避免。

name telephone
屈屈 18820030216
慧心 13268751479
达琳 15754874518
森贝 13698742277
米粒 13698652689
13026324789 困困
13658748974 毛毛
13265877158 米粒

UNION ALL

UNION会去除重复的行,如果我们想要保留重复的行,则可以使用UNION ALL。例如学生表和老师表中都有一名叫”米粒“的人,我们想要查询学校中所有人员的姓名,此时可以使用如下SQL语句:

1
2
3
SELECT name FROM students
UNION ALL
SELECT name FROM teachers;

可以看到UNION ALL会保留重复的行,最终结果含有两个名字为”米粒“的行:

name
屈屈
慧心
达琳
森贝
米粒
困困
毛毛
米粒

我们再和只使用UNION的结果对比下:

1
2
3
SELECT name FROM students
UNION
SELECT name FROM teachers;

其返回的结果为:

name
屈屈
慧心
达琳
森贝
米粒
困困
毛毛

可以看到UNION去除了重复的行,只保留了一个名字为”米粒“的行。

其中UNION也可以写作UNION DISTINCT,更明显的表示去除重复的行,两者是等价的:

1
2
3
SELECT name FROM students
UNION DISTINCT
SELECT name FROM teachers;

排序

我们可以对合并后的结果进行排序,例如我们想要按照id降序排列,可以使用如下SQL语句:

1
2
3
4
SELECT id, name, telephone FROM students
UNION ALL
SELECT id, name, telephone FROM teachers
ORDER BY id DESC;

可以看到id按照降序排列:

id name telephone
5 米粒 13698652689
4 森贝 13698742277
3 达琳 15754874518
3 米粒 13265877158
2 慧心 13268751479
2 毛毛 13658748974
1 屈屈 18820030216
1 困困 13026324789

从上述结果中可以看到ORDER的生效范围是合并后的全部数据,那么是否可能对两个查询结果分别排序呢?我们来看一个例子:

1
2
3
(SELECT id, name, telephone FROM students ORDER BY id DESC)
UNION ALL
(SELECT id, name, telephone FROM teachers ORDER BY id DESC)

这里使用括号来将两个查询扩起来,尝试分别对两个查询结果进行排序,我们来看下返回结果:

id name telephone
1 屈屈 18820030216
2 慧心 13268751479
3 达琳 15754874518
4 森贝 13698742277
5 米粒 13698652689
1 困困 13026324789
2 毛毛 13658748974
3 米粒 13265877158

可以看到实际并没有产生效果,并没有如预期id为5,4,3,2,1等等。也就是说对合并前的结果排序是没有效果的!

筛选

我们可以在合并后的结果上进行筛选,例如我们想要查询姓名为”屈屈“的人员信息,但是并不知道她是学生还是老师,则可以使用如下SQL语句:

1
2
3
4
5
6
7
SELECT * FROM
(
(SELECT id, name, telephone FROM students)
UNION ALL
(SELECT id, name, telephone FROM teachers)
) temp
WHERE name = '屈屈';

这里使用括号将两个查询扩起来,然后在外层查询中对合并后的结果进行筛选,其中temp为合并后的结果的别名,这样我们就可以在外层查询中对合并的结果进行筛选,就好像操作一张名为temp的表一样。将返回如下结果:

id name telephone
1 屈屈 18820030216

细心的你可能会疑惑,在排序小节我们可以对合并后的结果直接排序,那么是否可以类似的对合并后的结果进行筛选呢?我们来看一个例子:

1
2
3
4
SELECT id, name, telephone FROM students
UNION ALL
SELECT id, name, telephone FROM teachers
WHERE name = '屈屈';

我们仿照排序的写法,尝试在合并后的结果上进行筛选,我们来看下返回结果:

id name telephone
1 屈屈 18820030216
2 慧心 13268751479
3 达琳 15754874518
4 森贝 13698742277
5 米粒 13698652689

可以发现,其查询结果并不正确,所以在合并后的结果上进行筛选时,应当在外层查询中对合并后的结果进行筛选。

当然了,你也可以通过在每个查询中进行筛选,然后再合并,例如我们想要查询姓名为”屈屈“的学生和老师信息,可以使用如下SQL语句:

1
2
3
(SELECT id, name, telephone FROM students WHERE name = '屈屈')
UNION ALL
(SELECT id, name, telephone FROM teachers WHERE name = '屈屈');

将返回如下结果:

id name telephone
1 屈屈 18820030216