查询基础
0x01 SELECT 基础
基本查询
-- 查询所有列
SELECT * FROM users;
-- 查询特定列
SELECT username, email FROM users;
-- 查询表达式
SELECT username, age, age * 12 AS age_in_months FROM users;
别名
-- 列别名
SELECT username AS user_name, email AS user_email FROM users;
-- 表别名
SELECT u.username, u.email FROM users AS u;
-- 简写形式(省略 AS)
SELECT username user_name, email user_email FROM users u;
0x02 WHERE 条件过滤
比较运算符
-- 等于
SELECT * FROM users WHERE age = 25;
-- 不等于
SELECT * FROM users WHERE status != 'inactive';
SELECT * FROM users WHERE status <> 'inactive'; -- 另一种写法
-- 大于/小于
SELECT * FROM users WHERE age > 18;
SELECT * FROM users WHERE age >= 18;
SELECT * FROM users WHERE age < 65;
SELECT * FROM users WHERE age <= 65;
逻辑运算符
-- AND: 两个条件都满足
SELECT * FROM users WHERE age >= 18 AND age <= 65;
-- OR: 满足任一条件
SELECT * FROM users WHERE age < 18 OR age > 65;
-- NOT: 条件取反
SELECT * FROM users WHERE NOT status = 'inactive';
-- 组合使用
SELECT * FROM users WHERE (age >= 18 AND age <= 65) AND status = 'active';
范围查询
-- BETWEEN: 范围查询(包含边界)
SELECT * FROM users WHERE age BETWEEN 18 AND 65;
-- 等价于
SELECT * FROM users WHERE age >= 18 AND age <= 65;
-- NOT BETWEEN: 范围外查询
SELECT * FROM users WHERE age NOT BETWEEN 18 AND 65;
列表查询
-- IN: 在列表中
SELECT * FROM users WHERE country IN ('CN', 'US', 'JP');
-- 等价于
SELECT * FROM users WHERE country = 'CN' OR country = 'US' OR country = 'JP';
-- NOT IN: 不在列表中
SELECT * FROM users WHERE country NOT IN ('CN', 'US');
-- 子查询
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);
模式匹配
-- LIKE: 模式匹配
-- % 匹配任意字符(0个或多个)
-- _ 匹配单个字符
-- 以 'john' 开头
SELECT * FROM users WHERE username LIKE 'john%';
-- 以 'gmail.com' 结尾
SELECT * FROM users WHERE email LIKE '%@gmail.com';
-- 包含 'admin'
SELECT * FROM users WHERE username LIKE '%admin%';
-- 第二个字符是 'a'
SELECT * FROM users WHERE username LIKE '_a%';
-- NOT LIKE: 不匹配模式
SELECT * FROM users WHERE username NOT LIKE 'test%';
NULL 值处理
-- 检查 NULL 值
SELECT * FROM users WHERE phone IS NULL;
-- 检查非 NULL 值
SELECT * FROM users WHERE phone IS NOT NULL;
-- 注意:NULL 不能用 = 或 != 比较
-- 错误写法
SELECT * FROM users WHERE phone = NULL; -- 不会返回任何结果
-- 正确写法
SELECT * FROM users WHERE phone IS NULL;
0x03 ORDER BY 排序
基本排序
-- 升序排序(默认)
SELECT * FROM users ORDER BY age ASC;
SELECT * FROM users ORDER BY age; -- ASC 可省略
-- 降序排序
SELECT * FROM users ORDER BY age DESC;
多列排序
-- 先按 age 升序,再按 username 降序
SELECT * FROM users ORDER BY age ASC, username DESC;
-- 先按 status 分组,再按 created_at 降序
SELECT * FROM users ORDER BY status, created_at DESC;
表达式排序
-- 按表达式排序
SELECT * FROM users ORDER BY LENGTH(username);
-- 按别名排序
SELECT username, age * 12 AS age_in_months
FROM users
ORDER BY age_in_months DESC;
-- 按列位置排序(不推荐)
SELECT username, age FROM users ORDER BY 2; -- 按第2列排序
0x04 LIMIT 分页
基本分页
-- MySQL/PostgreSQL
SELECT * FROM users ORDER BY id LIMIT 10; -- 前10条
-- 跳过前5条,取10条
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 5;
-- MySQL 简写形式
SELECT * FROM users ORDER BY id LIMIT 5, 10; -- 跳过5条,取10条
分页查询示例
-- 第1页(每页10条)
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 0;
-- 第2页
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 10;
-- 第3页
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20;
-- 通用分页公式
-- LIMIT page_size OFFSET (page_number - 1) * page_size
0x05 DISTINCT 去重
基本去重
-- 去除重复值
SELECT DISTINCT country FROM users;
-- 多列去重
SELECT DISTINCT country, city FROM users;
聚合去重
-- 统计不重复的国家数量
SELECT COUNT(DISTINCT country) FROM users;
-- MySQL 也支持
SELECT COUNT(DISTINCT country) AS country_count FROM users;
0x06 条件表达式
CASE 表达式
-- 简单 CASE
SELECT
username,
status,
CASE status
WHEN 'active' THEN '活跃'
WHEN 'inactive' THEN '非活跃'
ELSE '未知'
END AS status_text
FROM users;
-- 搜索 CASE
SELECT
username,
age,
CASE
WHEN age < 18 THEN '未成年'
WHEN age < 65 THEN '成年人'
ELSE '老年人'
END AS age_group
FROM users;
IF 函数(MySQL)
-- MySQL IF 函数
SELECT
username,
age,
IF(age >= 18, '成年', '未成年') AS is_adult
FROM users;
-- IFNULL 函数
SELECT
username,
IFNULL(phone, '未填写') AS phone_display
FROM users;
COALESCE 函数
-- 返回第一个非 NULL 值
SELECT
username,
COALESCE(phone, email, '无联系方式') AS contact_info
FROM users;
0x07 集合操作
UNION
-- 合并结果集(去重)
SELECT username FROM users
UNION
SELECT username FROM admin_users;
-- 合并结果集(保留重复)
SELECT username FROM users
UNION ALL
SELECT username FROM admin_users;
INTERSECT 和 EXCEPT
-- PostgreSQL 支持
-- 交集
SELECT username FROM users
INTERSECT
SELECT username FROM admin_users;
-- 差集
SELECT username FROM users
EXCEPT
SELECT username FROM admin_users;
0x08 GROUP BY 分组查询
基本分组
-- 按单个字段分组
SELECT country, COUNT(*) AS user_count
FROM users
GROUP BY country;
-- 按多个字段分组
SELECT country, city, COUNT(*) AS user_count
FROM users
GROUP BY country, city;
聚合函数结合分组
-- 统计每个国家的用户数量和平均年龄
SELECT
country,
COUNT(*) AS user_count,
AVG(age) AS avg_age,
MAX(age) AS max_age,
MIN(age) AS min_age
FROM users
GROUP BY country;
-- 统计每个状态的订单数量和总金额
SELECT
status,
COUNT(*) AS order_count,
SUM(total_amount) AS total_amount,
AVG(total_amount) AS avg_amount
FROM orders
GROUP BY status;
HAVING 过滤分组
-- HAVING 用于过滤分组后的结果(WHERE 用于过滤分组前)
SELECT
country,
COUNT(*) AS user_count
FROM users
GROUP BY country
HAVING COUNT(*) > 10;
-- 组合使用:先过滤再分组再过滤
SELECT
department,
AVG(salary) AS avg_salary
FROM employees
WHERE status = 'active'
GROUP BY department
HAVING AVG(salary) > 5000;
按分组内时间排序取最新记录
在实际业务中,经常需要获取每个分组内最新的记录,例如:每个用户的最新订单、每个部门的最新员工等。
MySQL 8.0+ 窗口函数写法
-- 使用窗口函数 ROW_NUMBER() 按时间排序取每个用户的最新订单
SELECT * FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY created_at DESC
) AS rn
FROM orders
) t
WHERE rn = 1;
-- 每个用户最近3笔订单
SELECT * FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY created_at DESC
) AS rn
FROM orders
) t
WHERE rn <= 3;
PostgreSQL 写法
-- PostgreSQL 同样支持窗口函数
SELECT * FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY created_at DESC
) AS rn
FROM orders
) t
WHERE rn = 1;
-- 使用 DISTINCT ON(PostgreSQL 特有语法)
SELECT DISTINCT ON (user_id)
*
FROM orders
ORDER BY user_id, created_at DESC;
子查询写法(兼容旧版本)
-- 每个用户最新订单(子查询方式)
SELECT o.*
FROM orders o
WHERE o.created_at = (
SELECT MAX(created_at)
FROM orders
WHERE user_id = o.user_id
);
-- 每个部门的最新入职员工
SELECT e.*
FROM employees e
WHERE e.hire_date = (
SELECT MAX(hire_date)
FROM employees
WHERE department_id = e.department_id
);
关联子查询取最新时间
-- 获取每个用户的最新订单时间
SELECT
user_id,
(SELECT MAX(created_at) FROM orders WHERE user_id = u.user_id) AS latest_order_time
FROM users u;
-- 获取每个用户及其最新订单的完整信息(JOIN 方式)
SELECT u.*, o.*
FROM users u
LEFT JOIN (
SELECT o1.*
FROM orders o1
INNER JOIN (
SELECT user_id, MAX(created_at) AS max_date
FROM orders
GROUP BY user_id
) o2 ON o1.user_id = o2.user_id AND o1.created_at = o2.max_date
) o ON u.id = o.user_id;
参考
目录