本章将介绍如何组合 WHERE 子句以建立功能更强的更高级的搜索条件。我们还将学习如何使用 AND、OR、NOT 和 IN 操作符。
在之前的示例中,我们使用 WHERE 子句时仅设定了单一的过滤条件。为了实现更灵活、更精准的数据过滤,SQL 允许在 WHERE 子句中同时指定多个条件,这些条件可通过 AND 或 OR 两种方式组合,满足更复杂的筛选需求。
就像这样:
select * from user where gender is null and age>25 -- 或者这样 select * from user where gender is null or age>25 -- 甚至可以这样 select * from user where (gender is null or age>25) and create_time>'2025-10-01'
使用 WHERE 子句进行数据过滤时,可能需要多个条件参与过滤,才能准确找到我们需要的数据。
如果想要通过多个条件来筛选数据,可以借助 AND 操作符,在 WHERE 子句中添加更多过滤条件。例如:
> select * from user where gender='男' and age>25 user_id|name|gender|age|phone |email |address |create_time |update_time | -------+----+------+---+-----------+------------------+-------------+-------------------+-------------------+ 3|王五 |男 | 28|13800138002|wangwu@example.com|广州市天河区天河路385号|2025-01-09 02:26:00|2025-12-24 03:15:28| 5|孙七 |男 | 35|13800138004| |杭州市西湖区西湖路58号 |2025-01-10 14:36:00|2025-12-24 03:15:28| 2 row(s) fetched.
上面 SQL 语句的 WHERE 子句中包含两个条件,并且用 AND 关键字联结它们,意思是查询性别为“男”且年龄大于 25 岁的用户。
AND 关键字指示数据库只返回满足所有给定条件的行(即是“男”且年龄大于“25”岁)。如果某个用户是“男”,但它的年龄小于等于 25 岁,则不返回。类似,如果用户年龄大于 25 岁,但性别不是“男”,则也不被返回。
OR 操作符与 AND 操作符不同,它指示数据库返回匹配任一条件的行。例如:
> select * from user where gender='男' or age>25 user_id|name|gender|age|phone |email |address |create_time |update_time | -------+----+------+---+-----------+---------------------+---------------+-------------------+-------------------+ 1|张三 |男 | 25|13800138000|zhangsan@example.com |北京市朝阳区建国路88号 |2025-01-17 00:50:00|2025-12-24 03:15:28| 2|李四 |女 | 30|13800138001|lisi@example.com |上海市浦东新区张江路100号 |2025-01-30 05:47:00|2025-12-24 03:15:28| 3|王五 |男 | 28|13800138002|wangwu@example.com |广州市天河区天河路385号 |2025-01-09 02:26:00|2025-12-24 03:15:28| 5|孙七 |男 | 35|13800138004| |杭州市西湖区西湖路58号 |2025-01-10 14:36:00|2025-12-24 03:15:28| 6|周八 |女 | 27|13800138005|zhouba@example.com |成都市武侯区武侯祠大街231号|2025-01-10 16:41:00|2025-12-24 03:15:28| 7|吴九 | | 32|13800138006|wujiu@example.com |重庆市渝中区解放碑路18号 |2025-01-20 15:40:00|2025-12-24 03:15:28| 8|郑十 |女 | 29|13800138007|zhengshi@example.com |武汉市武昌区中南路99号 |2025-01-10 04:15:00|2025-12-24 03:15:28| 9|钱十一 |男 | 24|13800138008|qianshiyi@example.com|西安市雁塔区雁塔路15号 |2025-01-17 22:15:00|2025-12-24 03:15:28| 10|孙十二 |女 | 31|13800138009| |南京市玄武区玄武湖路78号 |2025-01-28 02:32:00|2025-12-24 03:15:28| 9 row(s) fetched.
上面 SQL 语句将告诉数据库返回性别是“男”,或者年龄大于“25”岁的用户信息,只要满足两个条件中的一个即可。
事实上,许多数据库在 OR WHERE 子句的第一个条件满足的情况下,不再计算第二个条件。在第一个条件满足时,不管第二个条件是否满足,相应的行都将被检索出来。例如:
select * from user where gender='男' or age>25
上面 SQL语句将返回所有性别为“男”的或者年龄大于“25”岁的用户。当用户性别为“男”时,会忽略年龄 25 这个条件,因为已经满足 WHERE 条件。再去对性别为“男”的用户进行年龄过滤是多余操作。类似编程中 if(true || false || false) 语句,第一个条件为 true 时,不会再去计算后续条件。
在 WHERE 子句中可包含任意数量的 AND 和 OR 操作符,支持将两者组合使用,以实现复杂且精细化的数据过滤。
不过,同时使用 AND 和 OR 会引出一个需要注意的逻辑问题。为直观说明这一问题,我们来看一个实际场景:假设需要筛选出年龄大于 25 岁,且性别为男性或女性的所有用户。以下 SELECT 语句尝试通过组合 AND 和 OR 操作符构建 WHERE 子句来实现这一需求:
> select * from user where age>25 and gender='男' or gender='女' user_id|name|gender|age|phone |email |address |create_time |update_time | -------+----+------+---+-----------+--------------------+---------------+-------------------+-------------------+ 2|李四 |女 | 30|13800138001|lisi@example.com |上海市浦东新区张江路100号 |2025-01-30 05:47:00|2025-12-24 03:15:28| 3|王五 |男 | 28|13800138002|wangwu@example.com |广州市天河区天河路385号 |2025-01-09 02:26:00|2025-12-24 03:15:28| 4|赵六 |女 | 22|13800138003|zhaoliu@example.com |深圳市南山区科技园10号 |2025-01-13 18:46:00|2025-12-24 03:15:28| 5|孙七 |男 | 35|13800138004| |杭州市西湖区西湖路58号 |2025-01-10 14:36:00|2025-12-24 03:15:28| 6|周八 |女 | 27|13800138005|zhouba@example.com |成都市武侯区武侯祠大街231号|2025-01-10 16:41:00|2025-12-24 03:15:28| 8|郑十 |女 | 29|13800138007|zhengshi@example.com|武汉市武昌区中南路99号 |2025-01-10 04:15:00|2025-12-24 03:15:28| 10|孙十二 |女 | 31|13800138009| |南京市玄武区玄武湖路78号 |2025-01-28 02:32:00|2025-12-24 03:15:28| 7 row(s) fetched.
仔细观察上面输出,包含了年龄为 22 岁的用户,显然,返回的行未按预期的进行过滤。
为什么会这样呢?原因在于计算的次序。SQL 在处理 OR 操作符前,优先处理 AND 操作符。当数据库执行上述 WHERE 子句时,它理解为 “年龄大于 25 岁且性别为男,或者性别为女”。换句话说,由于 AND 在计算次序中优先级更高,操作符被错误地组合了。类似如下:
> select * from user where (age>25 and gender='男') or gender='女' user_id|name|gender|age|phone |email |address |create_time |update_time | -------+----+------+---+-----------+--------------------+---------------+-------------------+-------------------+ 2|李四 |女 | 30|13800138001|lisi@example.com |上海市浦东新区张江路100号 |2025-01-30 05:47:00|2025-12-24 03:15:28| 3|王五 |男 | 28|13800138002|wangwu@example.com |广州市天河区天河路385号 |2025-01-09 02:26:00|2025-12-24 03:15:28| 4|赵六 |女 | 22|13800138003|zhaoliu@example.com |深圳市南山区科技园10号 |2025-01-13 18:46:00|2025-12-24 03:15:28| 5|孙七 |男 | 35|13800138004| |杭州市西湖区西湖路58号 |2025-01-10 14:36:00|2025-12-24 03:15:28| 6|周八 |女 | 27|13800138005|zhouba@example.com |成都市武侯区武侯祠大街231号|2025-01-10 16:41:00|2025-12-24 03:15:28| 8|郑十 |女 | 29|13800138007|zhengshi@example.com|武汉市武昌区中南路99号 |2025-01-10 04:15:00|2025-12-24 03:15:28| 10|孙十二 |女 | 31|13800138009| |南京市玄武区玄武湖路78号 |2025-01-28 02:32:00|2025-12-24 03:15:28| 7 row(s) fetched.
要解决该问题,可以使用圆括号明确地分组相应的操作符。例如:
> select * from user where age>25 and (gender='男' or gender='女') user_id|name|gender|age|phone |email |address |create_time |update_time | -------+----+------+---+-----------+--------------------+---------------+-------------------+-------------------+ 2|李四 |女 | 30|13800138001|lisi@example.com |上海市浦东新区张江路100号 |2025-01-30 05:47:00|2025-12-24 03:15:28| 3|王五 |男 | 28|13800138002|wangwu@example.com |广州市天河区天河路385号 |2025-01-09 02:26:00|2025-12-24 03:15:28| 5|孙七 |男 | 35|13800138004| |杭州市西湖区西湖路58号 |2025-01-10 14:36:00|2025-12-24 03:15:28| 6|周八 |女 | 27|13800138005|zhouba@example.com |成都市武侯区武侯祠大街231号|2025-01-10 16:41:00|2025-12-24 03:15:28| 8|郑十 |女 | 29|13800138007|zhengshi@example.com|武汉市武昌区中南路99号 |2025-01-10 04:15:00|2025-12-24 03:15:28| 10|孙十二 |女 | 31|13800138009| |南京市玄武区玄武湖路78号 |2025-01-28 02:32:00|2025-12-24 03:15:28| 6 row(s) fetched.
上面 SELECT 语句与前面 SELECT 语句的唯一区别是,这条语句中,将性别为男和女的条件使用括号进行括起来。
因为圆括号具有较 AND 或 OR 操作符高的计算次序,数据库首先过滤圆括号内的 OR 条件。
📢注意:在编写包含 AND、OR 操作符的 WHERE 子句时,必须通过圆括号显式指定操作符的分组逻辑。切勿依赖数据库默认的运算优先级 —— 即便当前默认次序与预期一致,也应主动使用圆括号。此举不会产生任何负面影响,且能有效消除语句的逻辑歧义,提升代码的可读性与可维护性。
IN 操作符用于指定条件匹配的取值范围,范围内的任一条件均可被匹配。使用时需将所有合法取值以逗号分隔,整体置于圆括号内,构成 IN 的取值清单。语法如下:
SELECT 列名1, 列名2, ... FROM 表名 WHERE 字段名 IN (值1, 值2, ..., 值n);
示例:查询姓名为 “张三”、“李四” 和 “孙七” 的用户信息。
> select * from user where name in ('张三', '李四', '孙七')
user_id|name|gender|age|phone |email |address |create_time |update_time |
-------+----+------+---+-----------+--------------------+--------------+-------------------+-------------------+
1|张三 |男 | 25|13800138000|zhangsan@example.com|北京市朝阳区建国路88号 |2025-01-17 00:50:00|2025-12-24 03:15:28|
2|李四 |女 | 30|13800138001|lisi@example.com |上海市浦东新区张江路100号|2025-01-30 05:47:00|2025-12-24 03:15:28|
5|孙七 |男 | 35|13800138004| |杭州市西湖区西湖路58号 |2025-01-10 14:36:00|2025-12-24 03:15:28|
3 row(s) fetched.其实,使用 OR 操作符也能实现 IN 操作符相同的功能,例如:
> select * from user where name='张三' or name='李四' or name='孙七' user_id|name|gender|age|phone |email |address |create_time |update_time | -------+----+------+---+-----------+--------------------+--------------+-------------------+-------------------+ 1|张三 |男 | 25|13800138000|zhangsan@example.com|北京市朝阳区建国路88号 |2025-01-17 00:50:00|2025-12-24 03:15:28| 2|李四 |女 | 30|13800138001|lisi@example.com |上海市浦东新区张江路100号|2025-01-30 05:47:00|2025-12-24 03:15:28| 5|孙七 |男 | 35|13800138004| |杭州市西湖区西湖路58号 |2025-01-10 14:36:00|2025-12-24 03:15:28| 3 row(s) fetched.
为什么使用 IN 操作符?IN 操作符具有如下优势:
当需指定较长的合法值清单时,IN 操作符的语法更清晰、直观。
使用 IN 操作符时,计算逻辑的优先级更易管控(因涉及的操作符更少)。
相较于由多个 OR 操作符组成的条件清单,IN 操作符的执行效率通常更高。
IN 操作符最突出的优势是可嵌套其他 SELECT 语句,能够动态构建 WHERE 子句。
WHERE 子句中 NOT 操作符的功能唯一且明确:对后续条件表达式执行逻辑否定。其否定作用覆盖范围不限于紧邻的单个条件,还可作用于 IN、BETWEEN、EXISTS 等组成的复合条件,实现反向数据筛选。
示例:查询所有性别不是 “男” 的用户信息
> select * from user where not gender='男' user_id|name|gender|age|phone |email |address |create_time |update_time | -------+----+------+---+-----------+--------------------+---------------+-------------------+-------------------+ 2|李四 |女 | 30|13800138001|lisi@example.com |上海市浦东新区张江路100号 |2025-01-30 05:47:00|2025-12-24 03:15:28| 4|赵六 |女 | 22|13800138003|zhaoliu@example.com |深圳市南山区科技园10号 |2025-01-13 18:46:00|2025-12-24 03:15:28| 6|周八 |女 | 27|13800138005|zhouba@example.com |成都市武侯区武侯祠大街231号|2025-01-10 16:41:00|2025-12-24 03:15:28| 8|郑十 |女 | 29|13800138007|zhengshi@example.com|武汉市武昌区中南路99号 |2025-01-10 04:15:00|2025-12-24 03:15:28| 10|孙十二 |女 | 31|13800138009| |南京市玄武区玄武湖路78号 |2025-01-28 02:32:00|2025-12-24 03:15:28| 5 row(s) fetched.
由于 NOT 无法单独使用(必须与其他操作符配合),其语法规则与其他操作符存在差异。与多数操作符仅能置于待过滤列后方不同,NOT 既可以放在列名前,也可置于条件表达式之后。
示例:将 NOT 操作符放到列名后面
> select * from user where name not in ('张三', '李四', '孙七')
user_id|name|gender|age|phone |email |address |create_time |update_time |
-------+----+------+---+-----------+---------------------+---------------+-------------------+-------------------+
3|王五 |男 | 28|13800138002|wangwu@example.com |广州市天河区天河路385号 |2025-01-09 02:26:00|2025-12-24 03:15:28|
4|赵六 |女 | 22|13800138003|zhaoliu@example.com |深圳市南山区科技园10号 |2025-01-13 18:46:00|2025-12-24 03:15:28|
6|周八 |女 | 27|13800138005|zhouba@example.com |成都市武侯区武侯祠大街231号|2025-01-10 16:41:00|2025-12-24 03:15:28|
7|吴九 | | 32|13800138006|wujiu@example.com |重庆市渝中区解放碑路18号 |2025-01-20 15:40:00|2025-12-24 03:15:28|
8|郑十 |女 | 29|13800138007|zhengshi@example.com |武汉市武昌区中南路99号 |2025-01-10 04:15:00|2025-12-24 03:15:28|
9|钱十一 |男 | 24|13800138008|qianshiyi@example.com|西安市雁塔区雁塔路15号 |2025-01-17 22:15:00|2025-12-24 03:15:28|
10|孙十二 |女 | 31|13800138009| |南京市玄武区玄武湖路78号 |2025-01-28 02:32:00|2025-12-24 03:15:28|
7 row(s) fetched.上面 SQL 语句的 NOT 操作符对 IN 条件进行取反,查询不包含 “张三”、“李四” 和 “孙七” 的用户数据。