SQL 基本查询语句我们介绍了基本的查询语句,学习了如何使用 SELECT 和 FROM 查询表中的数据。
但在实际应用中通常并不需要返回表中的全部数据,而只需要找出满足某些条件的结果。比如,某个部门中的员工或者某个产品最近几天的销售情况。在 SQL 中,可以通过查询条件实现数据的过滤。
在 SQL 语句中,使用关键字 WHERE 指定查询的过滤条件。以下语句只返回姓名为“刘备”的员工信息:
SELECT *
FROM employee
WHERE emp_name = '刘备';
其中,WHERE 位于 FROM 之后,用于指定一个或者多个过滤条件;只有满足条件的数据才会返回,其他数据将被忽略。该语句执行的结果如下:
在 SQL 中,WHERE 子句也被称为谓词(Predicate)。
这种通过查询条件过滤数据的操作在关系运算中被称为选择(Selection)。
在查询条件中,使用最多的就是数据的比较运算。
SQL 条件查询中使用比较运算符
比较运算符可以比较两个数值的大小,包括字符、数字以及日期类型的数据。下表列出了 SQL 中的各种比较运算符:
运算符 | 描述 | 示例 |
---|---|---|
= | 等于 | WHERE emp_id = 1 |
!= 或者 <> | 不等于 | WHERE sex != '男' |
> | 大于 | WHERE salary > 10000 |
>= | 大于等于 | WHERE hire_date >= DATE '2018-01-01' |
< | 小于 | WHERE bonus < 15000 |
<= | 小于等于 | WHERE dept_id <= 2 |
BETWEEN | 位于范围之内 | WHERE salary BETWEEN 10000 AND 15000 |
IN | 属于列表之中 | WHERE emp_name IN ('刘备', '关羽', '张飞') |
Oracle 中 ^= 运算符也表示不等于。
这些运算符的作用都比较好理解。我们来看一个日期数据的比较操作,假设想要知道哪些员工在 2018 年 1 月 1 日之后入职,可以使用以下查询:
-- 适用于 Oracle、MySQL 以及 PostgreSQL
SELECT emp_name, hire_date
FROM employee
WHERE hire_date >= DATE '2018-01-01';
其中,DATE '2018-01-01'
定义了一个日期类型的常量值。对于 SQL Server,指定日期时可以直接使用字符串字面值表示:
-- 适用于 SQL Server、MySQL 以及 PostgreSQL
SELECT emp_name, hire_date
FROM employee
WHERE hire_date >= '2018-01-01';
以上两个查询语句的结果如下:
除了我们常见的比较运算符之外,SQL 还提供了两个特殊的比较运算符:BETWEEN 和 IN。
BETWEEN 运算符
如果想要查找一个范围内的数据,可以使用 BETWEEN 运算符。以下示例查询月薪位于 10000 到 15000 之间的员工:
SELECT emp_name, salary
FROM employee
WHERE salary BETWEEN 10000 AND 15000;
该语句的结果如下:
需要注意的是,BETWEEN 包含了两端的值(10000 和 15000)。
IN 运算符
IN 运算符可以用于查找列表中的值。以下示例查询姓名为“刘备”、“关羽”或者“张飞”的员工:
SELECT emp_id, emp_name
FROM employee
WHERE emp_name IN ('刘备', '关羽', '张飞');
该查询的结果如下:
只要匹配列表中的任何一个值,都会返回结果。IN 运算符还有一个常见的用途就是子查询的结果匹配。
SQL 条件查询中使用逻辑运算符
刚才介绍了比较运算符,如果我们存在多个WHERE条件子句,可以使用逻辑运算符:
我们还是通过例子来看下这些逻辑运算符的使用,同样采用heros这张表的数据查询。
假设想要筛选最大生命值大于6000,最大法力大于1700的英雄,然后按照最大生命值和最大法力值之和从高到低进行排序。
SELECT name, hp_max, mp_max FROM heros WHERE hp_max > 6000 AND mp_max > 1700 ORDER BY (hp_max+mp_max) DESC
运行结果:(23条记录)
如果AND和OR同时存在WHERE子句中会是怎样的呢?假设我们想要查询最大生命值加最大法力值大于8000的英雄,或者最大生命值大于6000并且最大法力值大于1700的英雄。
SELECT name, hp_max, mp_max FROM heros WHERE (hp_max+mp_max) > 8000 OR hp_max > 6000 AND mp_max > 1700 ORDER BY (hp_max+mp_max) DESC
运行结果:(33条记录)
你能看出来相比于上一个条件查询,这次的条件查询多出来了10个英雄,这是因为我们放宽了条件,允许最大生命值+最大法力值大于8000的英雄显示出来。另外你需要注意到,当WHERE子句中同时存在OR和AND的时候,AND执行的优先级会更高,也就是说SQL会优先处理AND操作符,然后再处理OR操作符。
如果我们对这条查询语句OR两边的条件增加一个括号,结果会是怎样的呢?
SELECT name, hp_max, mp_max FROM heros WHERE ((hp_max+mp_max) > 8000 OR hp_max > 6000) AND mp_max > 1700 ORDER BY (hp_max+mp_max) DESC
运行结果:
所以当WHERE子句中同时出现AND和OR操作符的时候,你需要考虑到执行的先后顺序,也就是两个操作符执行的优先级。一般来说()优先级最高,其次优先级是AND,然后是OR。
如果我想要查询主要定位或者次要定位是法师或是射手的英雄,同时英雄的上线时间不在2016-01-01到2017-01-01之间。
SELECT name, role_main, role_assist, hp_max, mp_max, birthdate
FROM heros
WHERE (role_main IN ('法师', '射手') OR role_assist IN ('法师', '射手'))
AND DATE(birthdate) NOT BETWEEN '2016-01-01' AND '2017-01-01'
ORDER BY (hp_max + mp_max) DESC
你能看到我把WHERE子句分成了两个部分。第一部分是关于主要定位和次要定位的条件过滤,使用的是role_main in ('法师', '射手') OR role_assist in ('法师', '射手')
。这里用到了IN逻辑运算符,同时role_main
和role_assist
是OR(或)的关系。
第二部分是关于上线时间的条件过滤。NOT代表否,因为我们要找到不在2016-01-01到2017-01-01之间的日期,因此用到了NOT BETWEEN '2016-01-01' AND '2017-01-01'
。同时我们是在对日期类型数据进行检索,所以使用到了DATE函数,将字段birthdate转化为日期类型再进行比较。关于日期的操作,我会在下一篇文章中再作具体介绍。
这是运行结果(6条记录):