简介

在SQL语句中,数据查询语句SELECT是使用频率最高、用途最广的语句。

它由许多子句组成,通过这些子句可以完成选择、投影和连接等各种运算功能,得到用户所需的最终数据结果。

其中,选择运算是使用SELECT语句的WHERE子句来完成的。

投影运算是通过在SELECT子句中指定列名来完成的。

连接运算则表示把两个或两个以上的表中的数据连接起来,形成一个结果集合。

由于设计数据库时的关系规范化和数据存储的需要,许多信息被分散在数据库不同的表中,但是当显示一个完整的信息时,就需要将这些信息同时显示出来,这时就需要执行连接运算。完整的SELECT语法如下:


SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [HIGH_PRIORITY]
  [DISTINCT | DISTINCTROW | ALL]
  select_expression,...
  [INTO {OUTFILE | DUMPFILE} ’file_name’ export_options]
  [FROM table_references
  ][WHERE where_definition]
  [GROUP BY col_name,...]
  [HAVING where_definition]
  [ORDER BY {unsigned_integer | col_name | formula} ][ASC | DESC] ,...]
  [LIMIT ][offset,] rows]
  [PROCEDURE procedure_name] ]


简单查询

仅含有SELECT子句和FROM子句的查询是简单查询,SELECT子句和FROM子句是SELECT语句的必选项,也就是说每个SELECT语句都必须包含有这两个子句。其中,SELECT子句用于标识用户想要显示哪些列,通过指定列名或是用”*”号代表对应表的所有列;FROM子句则告诉数据库管理系统从哪里查找这些列,通过指定表名或是视图名称来描述。

下面的SELECT语句将显示表中所有的列和行:

SELECT * FROM emp;

其中,SELECT子句中的星号表示表中所有的列,该语句可以将指定表中的所有数据检索出来;FROM子句中的emp表示emp表,即整条SQL语句的含义是把emp表中的所有数据按行显示出来。

大多数情况下,SQL查询检索的行和列都比整个表的范围窄,用户需要检索比单个行和列多,但又比数据库所有行和列少的数据。这就是更加复杂的SELECT语句的由来。


1.       使用FROM子句指定表

SELECT语句的不同部分常用来指定要从数据库返回的数据。SELECT语句使用FROM子句指定查询中包含的行和列所在的表。FROM子句的语法格式如下:

[FROM{table_name|view_name}[(optimizer_hints)]

[,{table_name2|view_name2}[(optimizer_hints)]

[…,table_name16|view_name16][(optimizer_hints)]]]

可以在FROM子句中指定多个表,每个表使用逗号(,)与其他表名隔开,其格式如下所示:

SELECT * FROM t1,t2;


2.       使用SELECT指定列

用户可以指定查询表中的某些列而不是全部,这其实就是投影操作。

这些列名紧跟在SELECT关键词后面,与FROM子句一样,每个列名用逗号(,)隔开,其语法格式如下:

SELECTcolumn_name_1,…,column_name_n FROM …

利用SELECT指定列的方法可以改变列的顺序来显示查询的结果,甚至是可以通过在多个地方指定同一个列来多次显示同一列。


3.算术表达式

在使用SELECT语句时,对于数字数据和日期数据都可以使用算术表达式。在SELECT语句中可以使用的算术表运算符包括加(+)、减(-)、乘(*)、除(/)和括号。使用算术表达式的示例如下:

对emp表中薪资进行调整,所有人员的薪资增加10%,对应的SQL语句如下:

SELECTsalary*(1+0.1) FROM emp;

当使用SELECT语句查询数据库时,其查询结果集中的数据列名默认为表中的列名。为了提高查询结果集的可读性,可以在查询结果中为列指定标题。例如,在上面的示例中,将salary列乘以1.1后,计算上调10%后的雇员薪资、为了提高结果集的可读性,现在为它指定一个新的标题“上调后薪资”:


SELECTsalary*(1+0.1) 上调后薪资 from emp;

+-----------------+

| 上调后薪资|

+-----------------+

|11000.0 |

|16500.0 |

|22000.0 |

|27500.0 |

|33000.0 |

|27500.0 |

+-----------------+

提示:如果列标题中包含一些特殊的字符,例如空格等,则必须使用双引号将列标题括起来。


4.DISTINCT关键字

在默认情况下,结果集中包含检索到的所有数据航,而不管这些数据行是否重复出现。有的时候,当结果集中出现大量重复的行时,结果集会显得比较庞大,而不会带来有价值的信息,如在考勤记录中仅显示考勤的人员而补显示考勤的时间时,人员的名字会大量重复出现。若希望删除结果集中重复的行,则需在SELECT子句中使用DISTINCT关键字。

例如,查询emp表中的雇员都是来自哪些部门时:

SELECT DISTINCT deptno FROM emp;

若不使用关键字DISTINCT,则将在查询结果集中显示表中每一行的部门号,包括重复的部门编号


WHERE子句

WHERE子句用于筛选从FROM子句中返回的值,完成的选择操作。在SELECT语句中使用WHERE子句后,将对FROM子句指定的数据表中的行进行判断,只有满足WHERE子句中判断条件的行才会显示,而哪些不满足WHERE子句判断条件的行则不包括在结果集中。

在SELECT语句中,WHERE子句位于FROM子句之后,其语法格式如下所示:

SELECTcolumn_list FROM table_name WHERE conditional_expression

其中,conditional_expression为查询时返回记录应满足的判断条件。

1.       条件表达式

在conditional_expression中可以用运算符来对值进行比较,可用的运算符介绍如下:

A=B 表示A与B的值相等,则为TRUE;

A>B 表示若A的值大于B的值,则为TRUE;

A<B 表示若A的值小于B的值,则为TRUE;

A!=B或A<>B 表示若A的值不等于B的值,则为TRUE;


A LIKE B 其中,LIKE是匹配运算符。在这种判断条件中,若A的值匹配B的值,则该判断条件为TRUE。

在LIKE表达式中可以使用通配符。MySQL支持的通配符为:“%”代表0个、1个或多个任意字符,使用“_”代表一个任意字符

NOT <条件表达式> NOT运算符用于对结果取反。

例如,编写一个查询,判断所有phoneticize列第三个字母为‘A’的雇员信息:

SELECT * FROM emp WHERE phoneticize LIKE '__A%';

查询结果如下所示:

+----+--------+------+--------+--------+-------------+

| id |ename  | age  | deptno | salary | phoneticize |

+----+--------+------+--------+--------+-------------+

|  1 | 张三   |   21|      1 |  10000 | ZHANGSAN    |

|  4 | 赵六   |   31|      1 |  25000 | ZHAOLIU     |

+----+--------+------+--------+--------+-------------+

REGEXP<正则表达式>或RLIKE<正则表达式> 与正则表达式匹配的记录。

展正则表达式的一些字符是:

“.”匹配任何单个的字符。(单字节字符)

一个字符类“[…]”匹配在方括号内的任何字符。例如,“[abc]”匹配“a”、“b”或“c”。为了命名字符的一个范围,使用一个“-”。“[a-z]”匹配任何小写字母,而“[0-9]”匹配任何数字。

“ * ”匹配零个或多个在它前面的东西。例如,“x*”匹配任何数量的“x”字符,“[0-9]*”匹配的任何数量的数字,而“.*”匹配任何数量的任何东西。

正则表达式是区分大小写的,但是如果你希望,你能使用一个字符类匹配两种写法。例如,“[aA]”匹配小写或大写的“a”而“[a-zA-Z]”匹配两种写法的任何字母。

如果它出现在被测试值的任何地方,模式就匹配(只要他们匹配整个值,SQL模式匹配)。

为了定位一个模式以便它必须匹配被测试值的开始或结尾,在模式开始处使用“^”或在模式的结尾用“$”。

为了说明扩展正则表达式如何工作,上面所示的LIKE查询在下面使用REGEXP重写:

为了找出以“三”开头的名字,使用“^”匹配名字的开始。

FROM [user] WHEREu_name REGEXP ‘^三’;

将会把u_name为 “三脚猫”等等以“三”开头的记录全找出来。

为了找出以“三”结尾的名字,使用“$”匹配名字的结尾。

FROM [user] WHERE u_name REGEXP ‘三$’;

将会把u_name为“张三”,“张猫三”等等以“三”结尾的记录全找出来。

你也可以使用“{n}”“重复n次”操作符重写先前的查询:

FROM [user] WHEREu_name REGEXP ‘b{2}$’;

注意:如果是中文字符,可能在使用时需要注意一下。

例如,使用正则表达式进行上面的查询:

SELECT * FROM emp WHERE phoneticize REGEXP '^..A.*';

+----+--------+------+--------+--------+-------------+

| id |ename  | age  | deptno | salary | phoneticize |

+----+--------+------+--------+--------+-------------+

|  1 | 张三   |   21|      1 |  10000 | ZHANGSAN    |

|  4 | 赵六   |   31|      1 |  25000 | ZHAOLIU     |

+----+--------+------+--------+--------+-------------+

将上面查询中的^去掉后可深入理解^的作用;

SELECT * FROM emp WHERE phoneticize REGEXP '..A.*';

+----+--------+------+--------+--------+-------------+

| id |ename  | age  | deptno | salary | phoneticize |

+----+--------+------+--------+--------+-------------+

|  1 | 张三   |   21|      1 | 10000 | ZHANGSAN    |

|  4 | 赵六   |   31|      1 |  25000 | ZHAOLIU     |

|  6 | 孙八   |   37|      5 |  25000 | SUNBA       |

+----+--------+------+--------+--------+-------------+

上面使用^的SQL比不使用^的SQL查询结果少了最后一行,比较可以发现,使用^时匹配是从开始位置进行匹配,而不使用^则表示从任意处匹配,只要phoneticize值中包含..A即返回该记录,如最后一条记录中的SUNBA。使用RLIKE得到的结果和REGEXP一样。

NOT REGEXP<正则表达式>或NOT LIKE<正则表达式>  表示与正则表达式补匹配的记录。


2.       连接运算符

在WHERE子句中可以使用连接运算符将各个表达式关联起来组成复合判断条件。常用的连接运算符包括AND和OR。使用AND连接的运算符只有在AND左边和右边的表达式都为TRUE时,AND运算符才返回TRUE。

例如,查询出所有属于金融部(deptno=1),且薪资大于12000的雇员信息:

SELECT * FROM emp where deptno = 1 AND salary > 12000;

+----+--------+------+--------+--------+-------------+

| id |ename  | age  | deptno | salary | phoneticize |

+----+--------+------+--------+--------+-------------+

|  2 | 李四   |   22|      1 |  15000 | LISI        |

|  4 | 赵六   |   31|      1 |  25000 | ZHAOLIU     |

+----+--------+------+--------+--------+-------------+

如果使用OR运算符,则只要OR运算符左边的表达式或是OR运算符右边的表达式中有任一个为TRUE,那么OR运算符就要返回TRUE。

例如,查询出金融部(deptno=1)和人事部(deptno=2)下的员工信息:

SELECT * FROM emp where deptno = 1 OR deptno=2;

在复合判断条件中,需要注意运算符的优先级。MySQL会先运算优先级高的运算符,然后再运算优先级低的运算符,同级别的优先级则从左到右进行运算。这一规则符合人们日常生活中的规定。为了增加可读性,可以使用大括号将哥哥表达式括起来。


3.       NULL值

在数据库中,NULL值是一个特定的术语,用来描述记录中没有定义内容的字段值,通常我们称之为空。

NULL值是一个特殊的取值,使用“=”对NULL值进行查询是无法得到需要的结果的。

如SELECT * FROM emp WHEREphoneticize  = NULL;会得到空的查询结果。

可以使用IS NULL和IS NOT NULL 判断某列的值是否为NULL:

SELECT * FROM emp WHERE phoneticize  IS NULL;



ORDER BY子句和LIMIT关键字

在前面介绍的数据库检索技术中,只是把数据库中的数据从表中直接取出来。这是结果集中数据的排列顺序是由数据的存储顺序决定的。但是这种存储顺序经常不符合我们的查询需求。当查询一个比较大的表时,数据的显示会比较混乱。因此需要对检索到的结果集进行排序。在SELECT语句中,可以使用ORDER BY子句实现对查询的结果进行排序。

使用ORDER BY子句的语法形式如下:

SELECTcolumn_list

FROM table_name

ORDER BY[(order_by_expression[ASC|DESC])…]

其中,order_by_expression表示将要排序的列名或由列组成的表达式,关键字ASC指定按照升序排列,这也是默认的排列顺序,而关键字DESC指定按照降序排列。

例如,按照salary从高到低的顺序排列雇员信息:

SELECT * FROM emp ORDER BY salary DESC;

如果需要对多个列进行排列,只需要在ORDERBY 子句后指定多个列名。这样当输出排序结果时,首先根据第一类进行排序,当第一个列的值相同时,再对第二列进行比较排序。其他以此类推。

于排序后的记录,如果希望只显示一部分,而不是全部,这时,就可以使用LIMIT关键字来实现,LIMIT的语法如下:

SELECT…[LIMIT offset_start,row_count]

其中offset_start表示记录的其实偏移量,从0开始,row_count表示显示的行数。经常用来分页操作。


GROUP BY子句

GROUP BY子句用于在查询结果集中对记录进行分组,以汇总数据或者为整个分组显示单行的汇总信息。

使用GROUP BY子句和统计函数(又称聚合),可以实现对查询结果中每一组数据进行分类统计。

聚合操作的语法如下:

SELECT [field1,field2…] fun_name FROM table_name [WHERE CONDITION][GROUP BY condition [WITH ROLLUP]] [HAVING where_condition]

对其参数进行如下说明:

fun_name表示要做的聚合操作,也就是聚合函数,常用的有sum(求和)、count(*)(记录数)、max(最大值)、min(最小值)。

GROUP BY关键字表示要进行分类聚合的字段,比如要按照部门分类统计员工数量,部门就应该写在GROUP BY后面。

WITH ROLLUP是可选语法,表明是否对分类聚合后的结果再进行汇总。

HAVING关键字表示对分类后的结果再进行条件的过滤。

注意:having和where的区别在于,having是对聚合后的结果进行条件的过滤,而where是在聚合前就对记录进行过滤,如果逻辑允许,我们尽可能用where先过滤记录,这样因为结果集减小,将对聚合的效率大大提高,最后再根据逻辑看是否用having进行再过滤。

在使用GROUP BY子句时,必须满足下面的条件:
在SELECT子句的后面子可以有两类表达式:统计函数和进行分组的列名。
在SELECT子句中的列名必须是进行分组的列,除此之外添加其他的列名都是错误的,但是,GROUP BY子句后面的列名可以不出现在SELECT子句中。
如果使用了WHERE子句,那么所有参加分组计算的数据必须首先满足WHERE子句指定的条件。
在默认情况下,将按照GROUP BY子句指定的分组列升序排列,如果需要重新排列,可以使用ORDER BY子句指定新的排列顺序。


例如,要在emp表中统计公司的总人数:

select count(1) from emp;

在此基础上,要统计各个部门的人数:

select deptno,count(1) from emp group by deptno;

更细一些,既要统计各部门人数,又要统计总人数:

select deptno,count(1) from emp group by deptno with rollup;

统计人数大于1的部门:

select deptno,count(1) from emp group by deptno havingcount(1)>1;

最后统计所有员工的薪水总额、最高和最新薪水:

select sum(salary),max(salary),min(salary) from emp;


HAVING子句

HAVING子句通常与GROUP BY子句一起使用,在完成对分组结果统计后,可以使用HAVING子句对分组的结果做进一步的筛选。如果不使用GROUP BY子句,HAVING子句的功能与WHERE子句一样。HAVING子句和WHERE子句的相似之处就是都定义搜索条件,但是和WHERE子句不同,HAVING子句与组有关,而WHERE子句与单个的行有关。

如果在SELECT语句中使用的GROUPBY 子句,那么HAVING子句将应用域GROUP BY子句创建的那些组。如果指定了WHERE子句,而没有指定GROUP BY子句,那么HAVING子句将应用于WHERE子句的输出,并且整个输出被看作是一个组,如果在SELECT语句中既没有指定WHERE子句,也没有指定GROUP BY子句,那么HAVING子句将应用于FROM子句的输出,并且将其看作是一个组。

提示:对HAVING子句作用的理解有一个方法,就是记住SELECT语句中的子句的处理顺序。在SELECT语句中,首先由FROM子句找到数据表,WHERE子句则接收FROM子句输出的数据,而HAVING子句则接收来自GROUP BY、WHERE或FROM子句的输入。

SELECTdeptno,avg(salary),sum(salary),max(salary),min(salary),count(*) FROM emp GROUPBY deptno HAVING avg(salary) > 12000;


多表连接查询

到目前为止,大部分查询都几种在FROM子句仅使用一个表,但是,在设计数据库时,为了使数据库规范化,常常要把数据分别存放在不同的表中。这样可以消除数据冗余、插入异常和删除异常。但是在查询数据时,为了获取完整的信息就要将多个表连接起来,从多个表中查询数据。例如,为了获知雇员所在部门,可以在emp表中获取部门编号deptno,为了得到部门的名称还需要查询dept表。下面将对实现多表查询的方法一一进行介绍。

1.简单连接

连接查询实际是通过表与表之间相互关联的列进行数据的查询,对于关系数据库来说,连接是查询最主要的特征。简单连接使用逗号将两个或多个表进行连接,这是最简单,也是最常用的多表查询形式。

(1)      基本形式

简单连接仅是通过SELECT子句和FROM子句来连接多个表,其查询的结果是一个通过笛卡尔积所生成的表。所谓笛卡尔积所生成的表,就是由一个基表中的每一行与另一个基表的每一行连接在一起所生成的表,查询结果的行数是两个基表行数的积。

图解:

MySQL——查询sql总结:简单查询、连接查询、子查询_运算符


(2)      条件限定

在实际需求中,由于笛卡尔积中包含了大量的冗余信息,在一般情况下毫无意义。为了避免这种情况的出现,通常是在SELECT语句中提供了一个连接条件,过滤掉其中无意义的数据,从而使得满足用户的需求。

SELECT语句的WHERE子句提供了这个连接条件,可以有效避免笛卡尔积的出现。使用WHERE子句限定时,只有第一个表中的列与第二个表中对应的列相互匹配后才会在结果集中显示,这是连接查询中最常用的形式。

例如,查询出所有雇员的名字和所在部门名称,因为雇员名称和部门名称分别存放在emp和dept中,所以需要使用表连接来进行查询:

SELECT ename,deptname FROM emp ,dept WHERE emp.deptno =dept.deptno;

(3)      表别名

在以上演示中,我们发现,在多表查询时,如果多个表之间存在同名的列,则必须使用表名来限定列。但是,随着查询变的越来越复杂,语句会因为每次限定列时输入表名而变得冗长。因此,SQL语言提供了另一种机制——表别名。表别名是在FROM子句中用于各个表的“简单名称”,他们可以唯一地标识数据源。如上面的查询可以修改为:

SELECT e.ename,d.deptname FROM emp e  ,dept d WHERE e.deptno = d.deptno;

但是如果此时的SQL语句为;

SELECT e.ename,d.deptname FROM emp e  ,dept d WHERE emp.deptno = dept.deptno;

执行结果会报错,出现错误的原因是MySQL编译SQL语句时出现了问题。这里需要介绍一下SELECT语句中各子句执行的顺序,从而便可知道出错的真正原因。在SELECT语句的执行顺序中,FROM子句最先被执行,然后就是WHERE子句,最后才是SELECT子句。当在FROM子句中指定表别名后,表的真实名称将被替换。同时,其他的子句只能使用表别名来限定列。在上面的示例中,由于FROM子句已经用表别名覆盖了表的真实名称,当执行SELECT子句选择显示的列时,将无法找打真实表名称emp所限定的列


2.JOIN连接

除了使用逗号连接外,MySQL还支持使用关键字JOIN连接。使用JOIN连接的语法格式如下:

FROM join_table1join_type join_table2 [ON(join_condition)]

其中,join_table1指出参与表连接操作的表名;join_type指出连接类型,常用的连接包括内链接、自然连接、外链接和自连接。连接查询中的ON(join_condition)指出连接条件,它由被连接表中的列和比较运算符、逻辑运算符等构成。

(1)      内连接

内链接是一种常用的多表查询,一般用关键字INNERJOIN。其中可以省略INNER关键字,而只使用JOIN关键字表示内连接。内链接使用比较运算符时,在连接表的某些列之间进行比较,并列出表中与连接条件相匹配的数据行。

(2)      自然连接

 自然连接与内链接的功能相似,在使用自然连接查询多个表时,MySQL会将第一个表中的那些列与第二个表中具有相同名称的列进行连接。在自然连接中,用户不需要明确指定进行连接的列,系统会自动完成这一任务。

自然连接在实际的应用中很少,因为它有一个限制条件,即连接的各个表知啊件必须具有相同名称的列,而这在实际应用中可能和应用的实际含义发生矛盾。

(3)      外链接

使用内连接进行多表查询时,返回的查询结果集中仅包含符合查询条件和连接条件的行。内连接消除了与另一个表中的任何行不匹配的行,而外连接扩展了被连接的结果集,除返回所有匹配的行外,还会返回一部分或全部不匹配的行,这主要取决于外链接的种类。

外链接分为左外连接(LEFT JOIN或LEFT OUTERJOIN)、右外连接(RIGHT JOIN 或 RIGHT OUTER JOIN)和全外连接(FULL JOIN或FULL OUTER JOIN)三种。与内链接不同的是,外连接不只列出与连接条件相匹配的行,还列出左表(左外连接)、右表(右外连接)或两个表(全外链接)中所有符合搜索条件的数据行。

下面演示内链接和外链接的区别。

SELECT * FROM emp JOIN dept ON emp.deptno=dept.deptno WHERE emp.salary > 5000;
+----+--------+------+--------+--------+-------------+-----------+--------+
| id | ename | age | deptno | salary | phoneticize | deptname | deptno |
+----+--------+------+--------+--------+-------------+-----------+--------+
| 1 | 张三 | 21 | 1 | 10000 | ZHANGSAN | 金融部 | 1 |
| 2 | 李四 | 22 | 1 | 15000 | NULL | 金融部 | 1 |
| 3 | 王五 | 27 | 2 | 20000 | WANGWU | 人事部 | 2 |
| 4 | 赵六 | 31 | 1 | 25000 | ZHAOLIU | 金融部 | 1 |
| 5 | 周七 | 23 | 3 | 30000 | ZHOUQI | 事业部 | 3 |
+----+--------+------+--------+--------+-------------+-----------+--------+
外链接
SELECT * FROM emp LEFT JOIN dept ON emp.deptno=dept.deptno WHERE emp.salary > 5000;
+----+--------+------+--------+--------+-------------+-----------+--------+
| id | ename | age | deptno | salary | phoneticize | deptname | deptno |
+----+--------+------+--------+--------+-------------+-----------+--------+
| 1 | 张三 | 21 | 1 | 10000 | ZHANGSAN | 金融部 | 1 |
| 2 | 李四 | 22 | 1 | 15000 | NULL | 金融部 | 1 |
| 4 | 赵六 | 31 | 1 | 25000 | ZHAOLIU | 金融部 | 1 |
| 3 | 王五 | 27 | 2 | 20000 | WANGWU | 人事部 | 2 |
| 5 | 周七 | 23 | 3 | 30000 | ZHOUQI | 事业部 | 3 |
| 6 | 孙八 | 37 | 5 | 25000 | NULL | NULL | NULL |
+----+--------+------+--------+--------+-------------+-----------+--------+
同样的连接条件下,外链接比内链接多了一条记录,可以看出,左外连接的查询查询结果集中不仅包含相匹配的行,还包含左表中所有满足WHERE限制的行,而不论是否与右表相匹配。
查看排列的顺序也可以发现一些区别,内链接查询得到的结果顺序与存储顺序是一致的,但是左外连接有种按照连接条件分组的感觉,先是金融部,然后是人事部,接着是事业部。
从图来看更直观一些,内链接的执行图:


从图来看更直观一些,内链接的执行图:

MySQL——查询sql总结:简单查询、连接查询、子查询_数据_02


结果为:

MySQL——查询sql总结:简单查询、连接查询、子查询_数据_03


左外连接的执行图:

MySQL——查询sql总结:简单查询、连接查询、子查询_运算符_04


得到的结果:

MySQL——查询sql总结:简单查询、连接查询、子查询_数据_05


这样,可以猜测右外连接查询:

SELECT * FROM emp RIGHT JOIN dept ON emp.deptno=dept.deptno WHERE emp.salary > 5000;

的查询结果,ename的顺序应该和内链接一致:

 MySQL——查询sql总结:简单查询、连接查询、子查询_数据_06

至此,可以总结:

连接查询,在没有ORDER BY子句的情况下,内链接查询结果集顺序以JOIN前面的表为准,结果中不包含不匹配的项;左外连接查询以左表为主表,结果中包含左表中满足WHERE条件的不匹配的项,但是,结果集顺序以右表为准;右外连接查询以右表为主表,结果中包含右表中满足WHERE条件的不匹配的项,但是,结果集顺序以左表为准。


左外连接和右外连接的使用可以用下面的两个例子进行区别:

示例1:查询没有所属部门的员工姓名:

SELECT sub.enameFROM(SELECT ename,deptname FROM emp LEFT JOIN dept ON emp.deptno = dept.deptno)sub WHERE sub.deptname IS NULL;

+--------+

| ename  |

+--------+

| 孙八   |

+--------+

示例2:查询出部门下没有员工的部门名称:

SELECT sub.deptnameFROM(SELECT ename,deptname FROM emp RIGHT JOIN dept ON emp.deptno =dept.deptno) sub WHERE sub.ename IS NULL;

+-----------+

| deptname  |

+-----------+

| 市场部    |

+-----------+

(4)      自连接

有时候,用户可能会拥有自引用式外键,比如在树状关系数据存储时。


集合操作

集合操作就是将两个或多个SQL查询结果合并构成复合查询,以完成一些特殊的任务需求。结合操作主要由集合操作符实现,常用的集合操作符包括UNION(并运算)、UNION ALL。与Oracle相比,其集合操作符少了类似交和补的操作。

1.      UNION

UNION运算符可以将多个查询结果集相加,形成一个结果集,其结果等于集合运算中的并运算。即UNION运算符可以将第一个查询中的所有行与第二个查询中的所有行相加,并消除其中重复行形成的一个集合。

下面的示例中,第一个查询将选择所有phoneticize列以Z或者L开头的雇员信息,第二个查询将会选择所有phoneticize以Z或S开头的雇员信息。其结果是所有phoneticize以Z或L或S开头的雇员信息:

SELECT * FROM emp WHERE phoneticize like 'Z%' or phoneticizelike 'L%'

UNION

SELECT * FROM emp WHERE phoneticize like 'Z%' or phoneticizelike 'S%';

+----+--------+------+--------+--------+-------------+

| id |ename  | age  | deptno | salary | phoneticize |

+----+--------+------+--------+--------+-------------+

|  1 | 张三   |   21|      1 |  10000 | ZHANGSAN    |

|  2 | 李四   |   22|      1 |  15000 | LISI        |

|  4 | 赵六   |   31|      1 |  25000 | ZHAOLIU     |

|  5 | 周七   |   23|      3 |  30000 | ZHOUQI      |

|  6 | 孙八   |   37|      5 |  25000 | SUNBA       |

+----+--------+------+--------+--------+-------------+

注意:UNION运算会将集合中的重复记录滤除,这是UNION运算和UNION ALL运算唯一不同的地方。

2.      UNION ALL

使用UNION ALL 重复上述查询:

SELECT * FROM emp WHERE phoneticize like 'Z%' or phoneticizelike 'L%'

UNION ALL

SELECT * FROM emp WHERE phoneticize like 'Z%' or phoneticizelike 'S%';

+----+--------+------+--------+--------+-------------+

| id |ename  | age  | deptno | salary | phoneticize |

+----+--------+------+--------+--------+-------------+

|  1 | 张三   |   21|      1 |  10000 | ZHANGSAN    |

|  2 | 李四   |   22|      1 |  15000 | LISI        |

|  4 | 赵六   |   31|      1 |  25000 | ZHAOLIU     |

|  5 | 周七   |   23|      3 |  30000 | ZHOUQI      |

|  1 | 张三   |   21|      1 |  10000 | ZHANGSAN    |

|  4 | 赵六   |   31|      1 |  25000 | ZHAOLIU     |

|  5 | 周七   |   23|      3 |  30000 | ZHOUQI      |

|  6 | 孙八   |   37|      5 |  25000 | SUNBA       |

+----+--------+------+--------+--------+-------------+



子查询

子查询和连接查询一样,都提供了使用单个查询访问多个表中数据的方法。子查询在其他查询的基础上,提供一种进一步有效的方式来表示WHERE子句中的条件。子查询是一个SELECT语句,它可以在SELECT、INSERT、UPDATE或DELETE语句中使用。虽然大部分子查询是在SELECT语句的WHERE子句中实现,但实际上它的应用不仅仅局限于此。例如,也可以在SELECT和HAVING子句中使用子查询。

1.      IN关键字

使用IN关键字可以将原表中特定列的值与子查询返回的结果集中的值进行比较,如果某行的特定列的值存在,则在SELECT语句的查询结果中就包含这一行。

示例:使用子查询查看所有部门级别大于2的雇员信息:

SELECT * FROM emp WHERE emp.deptno IN (SELECT d.deptno FROMdept d WHERE d.degree > 2);

该查询语句执行顺序为:首先执行括号内的子查询,然后再执行外层查询。仔细观察括号内的子查询,可以看到该子查询的作用仅提供了外层查询WHERE子句所使用的限定条件。

单独执行该子查询则会将dept表中degree大于2的部门编号返回。

这些返回值将由IN关键字用来与emp表中每一行的deptno列进行比较,若列值存在于这些返回值中,则外层查询会在结果集中显示该行。

2.      EXISTS关键字

在一些情况下,只需要考虑是否满足判断条件,而数据本身并不重要,这时就可以使用EXISTS关键字来定义子查询。EXISTS关键字只注重子查询是否返回行,如果子查询返回一个或多个行,那么EXISTS便返回TRUE,否则为FALSE。

SELECT * FROM emp WHERE EXISTS(SELECT * FROM dept WHEREemp.deptno=dept.deptno and dept.degree>1);

3.      比较运算符

如果可以确认子查询返回的结果只包含一个单值,那么可以直接使用比较运算符连接子查询。经常使用的比较运算符包括等于(=)、不等于(<>或!=)、小于(<)、大于(>)、小于等于(<=)和大于等于(>=)。