准备示例数据
本章示例基于以下表结构和数据,建议先在本地执行:
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
age INT DEFAULT 0,
city VARCHAR(50),
salary DECIMAL(10, 2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO users (name, email, age, city, salary) VALUES
('张三', 'zhangsan@example.com', 28, '北京', 15000.00),
('李四', 'lisi@example.com', 32, '上海', 20000.00),
('王五', 'wangwu@example.com', 25, '广州', 12000.00),
('赵六', 'zhaoliu@example.com', 35, '北京', 25000.00),
('孙七', 'sunqi@example.com', 22, '深圳', 8000.00),
('周八', 'zhouba@example.com', 29, '上海', 18000.00),
('吴九', 'wujiu@example.com', 31, NULL, 16000.00),
('郑十', 'zhengshi@example.com', 27, '广州', 13000.00);
1. SELECT 基础
SELECT 是 SQL 中最常用的语句,用于从表中查询数据。它的基本结构如下:
-- 查询所有列
SELECT * FROM users;
-- 查询指定列
SELECT name, email FROM users;
-- 查询指定列并指定顺序
SELECT email, name, age FROM users;
列别名(AS)
使用 AS 给列取别名,让结果更易读:
-- 使用别名
SELECT name AS 姓名, email AS 邮箱, age AS 年龄 FROM users;
-- AS 关键字可以省略
SELECT name 姓名, email 邮箱, age 年龄 FROM users;
-- 别名中有空格或特殊字符时需要加引号
SELECT name AS '用户 姓名', salary AS '月薪(元)' FROM users;
去重(DISTINCT)
使用 DISTINCT 去除重复值:
-- 查询所有不重复的城市
SELECT DISTINCT city FROM users;
-- DISTINCT 作用于所有列的组合
SELECT DISTINCT city, age FROM users;
计算列
SELECT 中可以使用表达式和计算:
-- 计算年薪
SELECT name, salary, salary * 12 AS 年薪 FROM users;
-- 使用字符串拼接
SELECT CONCAT(name, ' (', city, ')') AS 用户信息 FROM users;
2. WHERE 条件过滤
WHERE 子句用于筛选满足条件的行。SQL 提供了丰富的比较和逻辑运算符。
比较运算符
-- 等于
SELECT * FROM users WHERE city = '北京';
-- 不等于(两种写法)
SELECT * FROM users WHERE city != '北京';
SELECT * FROM users WHERE city <> '北京';
-- 大于、小于、大于等于、小于等于
SELECT * FROM users WHERE age > 30;
SELECT * FROM users WHERE salary >= 15000;
逻辑运算符
-- AND:所有条件都满足
SELECT * FROM users WHERE city = '北京' AND age > 30;
-- OR:满足任一条件
SELECT * FROM users WHERE city = '北京' OR city = '上海';
-- NOT:取反
SELECT * FROM users WHERE NOT city = '北京';
-- 组合使用(注意优先级,AND 高于 OR)
SELECT * FROM users WHERE city = '北京' OR (city = '上海' AND age > 30);
-- 使用括号明确优先级
SELECT * FROM users WHERE (city = '北京' OR city = '上海') AND age > 25;
IN 与 BETWEEN
-- IN:匹配多个值(等同于多个 OR)
SELECT * FROM users WHERE age IN (25, 28, 32);
-- NOT IN:排除指定值
SELECT * FROM users WHERE city NOT IN ('北京', '上海');
-- BETWEEN:范围查询(包含两端)
SELECT * FROM users WHERE age BETWEEN 20 AND 30;
-- 等同于
SELECT * FROM users WHERE age >= 20 AND age <= 30;
LIKE 模糊匹配
-- % 匹配任意多个字符(包括零个)
SELECT * FROM users WHERE name LIKE '张%'; -- 以"张"开头
SELECT * FROM users WHERE email LIKE '%example%'; -- 包含"example"
SELECT * FROM users WHERE name LIKE '%三'; -- 以"三"结尾
-- _ 匹配恰好一个字符
SELECT * FROM users WHERE name LIKE '张_'; -- "张"后面恰好一个字符
SELECT * FROM users WHERE name LIKE '__'; -- 恰好两个字符的名字
NULL 判断
-- 查找城市为空的用户
SELECT * FROM users WHERE city IS NULL;
-- 查找城市不为空的用户
SELECT * FROM users WHERE city IS NOT NULL;
重要:SQL 中 NULL 不等于任何值,包括它自身。WHERE city = NULL 永远不会匹配到任何行,必须使用 IS NULL 来判断。同样,NULL != NULL 的结果是 NULL(不是 TRUE),这被称为三值逻辑(TRUE、FALSE、NULL)。
3. ORDER BY 排序
ORDER BY 子句用于对查询结果排序。默认为升序(ASC),也可以指定降序(DESC)。
基本排序
-- 按年龄升序(默认)
SELECT * FROM users ORDER BY age;
SELECT * FROM users ORDER BY age ASC;
-- 按薪资降序
SELECT * FROM users ORDER BY salary DESC;
-- 按创建时间降序(最新的在前)
SELECT * FROM users ORDER BY created_at DESC;
多列排序
可以按多列排序,第一列相同时按第二列排序:
-- 先按城市升序,同一城市内按薪资降序
SELECT * FROM users ORDER BY city ASC, salary DESC;
-- 先按年龄段排序,再按姓名排序
SELECT name, age, city FROM users ORDER BY age DESC, name ASC;
中文排序
默认排序规则可能不符合中文拼音排序习惯,可以使用 COLLATE 或 CONVERT:
-- 按拼音排序(使用 GBK 转换)
SELECT * FROM users ORDER BY CONVERT(name USING gbk);
-- 使用 COLLATE 指定排序规则
SELECT * FROM users ORDER BY name COLLATE utf8mb4_zh_0900_as_cs;
注意:ORDER BY 中的 NULL 值在 MySQL 中默认排在最前面(升序时)。如果需要将 NULL 排在最后,可以使用 ORDER BY ISNULL(city), city 或 ORDER BY city IS NULL, city。
4. LIMIT 分页
LIMIT 子句用于限制返回的行数,是实现分页查询的核心。
基本用法
-- 只返回前 3 条
SELECT * FROM users LIMIT 3;
-- 跳过前 2 条,返回接下来的 3 条
SELECT * FROM users LIMIT 3 OFFSET 2;
-- 简写形式:LIMIT offset, count
SELECT * FROM users LIMIT 2, 3; -- 等同于 LIMIT 3 OFFSET 2
分页查询
典型的分页查询场景(每页 3 条):
-- 第 1 页
SELECT * FROM users ORDER BY id LIMIT 3 OFFSET 0;
-- 第 2 页
SELECT * FROM users ORDER BY id LIMIT 3 OFFSET 3;
-- 第 3 页
SELECT * FROM users ORDER BY id LIMIT 3 OFFSET 6;
-- 通用公式:LIMIT pageSize OFFSET (pageNum - 1) * pageSize
TOP-N 查询
-- 查找薪资最高的 3 个用户
SELECT name, salary FROM users ORDER BY salary DESC LIMIT 3;
-- 查找年龄最小的用户
SELECT * FROM users ORDER BY age ASC LIMIT 1;
🔄 不同数据库的分页语法
| 数据库 | 分页语法 |
| MySQL | SELECT * FROM t LIMIT 10 OFFSET 20 |
| PostgreSQL | SELECT * FROM t LIMIT 10 OFFSET 20(语法相同) |
| SQL Server | SELECT TOP 10 * FROM t 或 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY |
| Oracle | WHERE ROWNUM <= 10 或 12c+ 支持 FETCH FIRST 10 ROWS ONLY |
性能提示:当 OFFSET 很大时(如第 10000 页),查询性能会显著下降,因为数据库仍需扫描并跳过大量行。对于大数据量分页,建议使用基于游标(cursor-based)的分页方式,如 WHERE id > last_seen_id LIMIT 10。
5. 常用函数
MySQL 提供了丰富的内置函数,覆盖字符串处理、数值运算、日期操作和条件判断等场景。
字符串函数
-- CONCAT:字符串拼接
SELECT CONCAT(name, ' - ', city) AS info FROM users;
-- CONCAT_WS:用分隔符拼接(自动跳过 NULL)
SELECT CONCAT_WS(', ', name, city, email) AS info FROM users;
-- SUBSTRING:截取子串(位置从 1 开始)
SELECT SUBSTRING(email, 1, INSTR(email, '@') - 1) AS username FROM users;
-- UPPER / LOWER:大小写转换
SELECT UPPER(email) FROM users;
SELECT LOWER(name) FROM users;
-- TRIM:去除首尾空格
SELECT TRIM(' hello '); -- 'hello'
SELECT TRIM(LEADING '0' FROM '00123'); -- '123'
-- LENGTH / CHAR_LENGTH:字节长度 vs 字符长度
SELECT name, LENGTH(name) AS 字节数, CHAR_LENGTH(name) AS 字符数 FROM users;
-- REPLACE:替换字符串
SELECT REPLACE(email, 'example.com', 'test.com') FROM users;
数值函数
-- ROUND:四舍五入
SELECT ROUND(salary / 3, 2) FROM users; -- 保留 2 位小数
-- CEIL / FLOOR:向上取整 / 向下取整
SELECT CEIL(15.2); -- 16
SELECT FLOOR(15.8); -- 15
-- ABS:绝对值
SELECT ABS(-100); -- 100
-- MOD:取模(求余数)
SELECT MOD(17, 5); -- 2
-- RAND:随机数(0 到 1 之间)
SELECT * FROM users ORDER BY RAND() LIMIT 1; -- 随机取一行
日期函数
-- NOW():当前日期时间
SELECT NOW(); -- 2026-04-16 10:30:00
-- CURDATE():当前日期
SELECT CURDATE(); -- 2026-04-16
-- DATE_FORMAT:格式化日期
SELECT DATE_FORMAT(created_at, '%Y年%m月%d日') FROM users;
SELECT DATE_FORMAT(NOW(), '%H:%i:%s'); -- 10:30:00
-- DATEDIFF:日期差(天数)
SELECT name, DATEDIFF(NOW(), created_at) AS 注册天数 FROM users;
-- DATE_ADD / DATE_SUB:日期加减
SELECT DATE_ADD(NOW(), INTERVAL 7 DAY); -- 7 天后
SELECT DATE_SUB(NOW(), INTERVAL 1 MONTH); -- 1 个月前
SELECT DATE_ADD(NOW(), INTERVAL 2 HOUR); -- 2 小时后
-- YEAR / MONTH / DAY:提取年月日
SELECT YEAR(created_at), MONTH(created_at), DAY(created_at) FROM users;
条件函数
-- IF:简单条件判断
SELECT name, salary,
IF(salary >= 15000, '高薪', '普通') AS 薪资等级
FROM users;
-- IFNULL / COALESCE:处理 NULL 值
SELECT name, IFNULL(city, '未填写') AS city FROM users;
SELECT name, COALESCE(city, '未填写') AS city FROM users;
-- CASE WHEN:多条件分支(类似 switch/if-else)
SELECT name, salary,
CASE
WHEN salary >= 20000 THEN '高薪'
WHEN salary >= 15000 THEN '中等'
WHEN salary >= 10000 THEN '一般'
ELSE '低薪'
END AS 薪资等级
FROM users;
-- CASE WHEN 的简写形式(等值判断)
SELECT name, city,
CASE city
WHEN '北京' THEN '华北'
WHEN '上海' THEN '华东'
WHEN '广州' THEN '华南'
WHEN '深圳' THEN '华南'
ELSE '其他'
END AS 区域
FROM users;
兼容性注意:MySQL 的函数并非所有数据库都支持。例如 IFNULL 是 MySQL 专有的,PostgreSQL 用 COALESCE,SQL Server 用 ISNULL。CASE WHEN 语法是 SQL 标准,所有数据库都支持。编写跨数据库兼容的 SQL 时,优先使用标准语法。
6. 本章要点
🔍 SELECT 查询
用 SELECT 查询数据,* 表示所有列,AS 定义别名,DISTINCT 去重。尽量指定具体列名,避免使用 SELECT *。
🔧 WHERE 过滤
使用比较运算符、AND/OR/NOT、IN、BETWEEN、LIKE 进行条件过滤。NULL 必须用 IS NULL 判断。
📊 ORDER BY 排序
ASC 升序(默认),DESC 降序。支持多列排序和中文排序。NULL 值默认排在最前面。
📄 LIMIT 分页
LIMIT n 限制行数,LIMIT n OFFSET m 实现分页。大偏移量性能差,推荐游标分页。
🧮 常用函数
字符串用 CONCAT/SUBSTRING/TRIM,数值用 ROUND/CEIL/FLOOR,日期用 NOW/DATE_FORMAT/DATEDIFF。
🔀 条件表达式
IF 处理简单条件,CASE WHEN 处理多分支,IFNULL/COALESCE 处理 NULL 值。优先使用 SQL 标准语法。