知识点简介
SELECT 和 WHERE 是 SQL 最核心的两个子句------一个决定「看什么列」,一个决定「看哪些行」。
看似简单,但里边藏着不少细节:去重、多条件组合、NULL 处理、性能陷阱...都是面试必考、实战必用的知识点。
核心语法详解
1️⃣ SELECT --- 选择列
sql
-- 查询所有列(生产环境慎用)
SELECT * FROM users;
-- 查询指定列
SELECT id, name, email FROM users;
-- 列别名(AS 可省略)
SELECT name AS 用户名, email AS 邮箱 FROM users;
-- 表达式计算
SELECT name, salary, salary * 1.1 AS 加薪后 FROM employees;
-- 常量列
SELECT '员工' AS 类型, name FROM users;
💡 最佳实践 :生产环境避免
SELECT *,一是明文传输多余字段有安全风险,二是索引覆盖会失效(回表查询)。
2️⃣ DISTINCT --- 去重
sql
-- 单列去重
SELECT DISTINCT department FROM employees;
-- 多列去重(两列组合唯一才视为重复)
SELECT DISTINCT department, position FROM employees;
-- 聚合中去重
SELECT COUNT(DISTINCT department) FROM employees;
⚠️
DISTINCT是对结果集做全排序去重,数据量大时性能差。如果只是「有没有重复记录」,用EXISTS替代。
3️⃣ WHERE --- 筛选行
sql
-- 精确匹配
SELECT * FROM users WHERE id = 100;
-- 字符串匹配(注意要用单引号)
SELECT * FROM users WHERE name = '张三';
-- 比较运算符
SELECT * FROM products WHERE price > 100;
SELECT * FROM orders WHERE amount >= 500;
SELECT * FROM tasks WHERE created_at < '2026-01-01';
4️⃣ AND / OR / NOT --- 逻辑组合
sql
-- AND:同时满足(优先级高于 OR)
SELECT * FROM employees
WHERE department = '技术部' AND salary > 10000;
-- OR:满足任一
SELECT * FROM employees
WHERE department = '技术部' OR department = '产品部';
-- NOT:取反
SELECT * FROM products
WHERE NOT discontinued;
-- 组合使用(AND 优先于 OR,建议加括号)
SELECT * FROM orders
WHERE (status = '已支付' OR status = '已发货')
AND amount > 500;
⚠️ 易错点 :SQL 里
AND优先级高于OR,WHERE a=1 OR b=2 AND c=3等价于WHERE a=1 OR (b=2 AND c=3),不是你直觉的(a=1 OR b=2) AND c=3。
5️⃣ IN / BETWEEN / IS NULL
sql
-- IN:值列表匹配
SELECT * FROM employees
WHERE department IN ('技术部', '产品部', '设计部');
-- NOT IN:排除(注意 NULL 陷阱!)
SELECT * FROM employees
WHERE department NOT IN ('实习生'); -- 安全,无 NULL
-- BETWEEN:范围匹配(闭区间,包含两端)
SELECT * FROM products
WHERE price BETWEEN 100 AND 500;
-- IS NULL:检查空值(不能写 = NULL!)
SELECT * FROM customers
WHERE phone IS NULL;
-- IS NOT NULL
SELECT * FROM customers
WHERE phone IS NOT NULL;
⚠️ 经典面试题 :
WHERE col NOT IN (1, 2, NULL)的结果永远是空!因为col <> NULL的结果是UNKNOWN,WHERE只保留TRUE的记录。
6️⃣ 执行顺序(面试必问)
sql
SELECT department, COUNT(*) AS 人数 -- 3.
FROM employees -- 1.
WHERE status = '在职' -- 2.
ORDER BY 人数 DESC; -- 4.
执行顺序 :FROM → WHERE → SELECT → ORDER BY
这就是为什么 WHERE 中可以使用表的原始字段,但不能使用 SELECT 中的别名:
sql
-- ❌ 错误:WHERE 执行时别名 含税价 还未定义
SELECT name, salary * 1.13 AS 含税价
FROM employees
WHERE 含税价 > 10000;
-- ✅ 正确:使用原始列或子查询
SELECT name, salary * 1.13 AS 含税价
FROM employees
WHERE salary * 1.13 > 10000;
只有 HAVING 和 ORDER BY 可以使用别名(因为它们在第 3 步之后执行)。
实战案例
场景:员工查询系统
sql
-- 建表
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(50),
department VARCHAR(50),
salary NUMERIC(10,2),
join_date DATE,
is_active BOOLEAN DEFAULT true
);
-- 插入测试数据
INSERT INTO employees (name, department, salary, join_date, is_active) VALUES
('张三', '技术部', 15000, '2023-03-15', true),
('李四', '技术部', 18000, '2022-06-01', true),
('王五', '产品部', 12000, '2024-01-10', true),
('赵六', '设计部', 9000, '2024-07-20', false),
('钱七', '产品部', 11000, '2023-11-05', true);
-- 实战 1:技术部月薪过万的在职员工
SELECT name, salary
FROM employees
WHERE department = '技术部'
AND salary >= 10000
AND is_active = true;
-- 结果:
-- | name | salary |
-- | 张三 | 15000 |
-- | 李四 | 18000 |
-- 实战 2:2023年入职的非技术部员工
SELECT name, department, join_date
FROM employees
WHERE join_date BETWEEN '2023-01-01' AND '2023-12-31'
AND department != '技术部';
-- 结果:
-- | name | department | join_date |
-- | 赵六 | 设计部 | 2024-... |
-- 不对,赵六是2024年入职的。实际结果应该是:
-- | name | department | join_date |
-- | 钱七 | 产品部 | 2023-11-05|
-- 实战 3:工资 9000-12000 的在职员工
SELECT name, department, salary
FROM employees
WHERE salary BETWEEN 9000 AND 12000
AND is_active = true;
-- 结果:
-- | name | department | salary |
-- | 王五 | 产品部 | 12000 |
-- | 钱七 | 产品部 | 11000 |
-- ⚠️ BETWEEN 包含两端值,12000 也算在内
常见坑点 / 注意事项
| 坑点 | 错误写法 | 正确写法 |
|---|---|---|
| NULL 判断 | col = NULL |
col IS NULL |
| 字符串值 | WHERE name = "张三" |
WHERE name = '张三' |
| BETWEEN 边界 | 以为不包括端点 | 实际是闭区间 [a, b] |
| AND/OR 优先级 | a=1 OR b=2 AND c=3 |
(a=1 OR b=2) AND c=3 |
| WHERE 用别名 | WHERE alias > 10 |
子查询或重写条件 |
| NOT IN 有 NULL | NOT IN (1, NULL) 空结果 |
用 NOT EXISTS 替代 |
| DISTINCT 性能 | 大表无脑 DISTINCT | 优先用 EXISTS 或聚合 |
| 浮点相等 | WHERE price = 0.1 |
用 ABS(price - 0.1) < 0.001 |
📌 今日总结
SELECT决定输出列,WHERE决定筛选行- 执行顺序:
FROM→WHERE→SELECT→ORDER BY - 三个特殊值列表:
IN、BETWEEN、IS NULL - NULL 的坑:任何 NULL 参与的比较都是
UNKNOWN - 括号控制优先级,不到万不得已别依赖默认优先级