EP01. “SQL 查询基础:SELECT、WHERE、JOIN”
🔒 登录后可标记已读这篇笔记讲开发者最常用到的 SQL 查询基础——SELECT 读数据、WHERE 过滤条件、JOIN 合并多张表。 也会讲排序分页(ORDER BY + LIMIT)和聚合分组(Aggregations & Grouping)怎么写。 不需要 DBA 级别的知识,但这些是写出「不会烂」的查询必备的基本功。 前置知识:知道数据库有表(table)、栏位(column)、行(row)这些基本概念即可。
重点内容
为什么 2026 年 SQL 还是很重要
ORM(对象关系映射)处理简单的增删改查(CRUD)没问题,但事情一复杂:
- ORM 生成的查询会变得很烂
- 需要用到 JOIN、聚合、窗口函数(window functions)
- 效能排查最终还是要回去读实际的 SQL
- 数据库常常就是效能瓶颈所在
💡 SQL 学一次,到处都能用——就连各种 "NoSQL" 数据库,最后也几乎都会加上类似 SQL 的查询方式。
SELECT:读数据
-- 基本用法
SELECT * FROM users;
-- 指定栏位(永远优先这样写,不要用 *)
SELECT id, email, name FROM users;
-- 用别名(alias)
SELECT
u.id AS user_id,
u.email,
COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
WHERE:过滤数据
-- 基本条件
SELECT * FROM users WHERE active = true;
SELECT * FROM products WHERE price > 100;
-- 多重条件
SELECT * FROM orders
WHERE status = 'completed'
AND created_at >= '2026-01-01'
AND total_amount > 50;
-- 模糊匹配
SELECT * FROM users WHERE email LIKE '%@gmail.com';
SELECT * FROM users WHERE name LIKE 'J%'; -- 以 J 开头
SELECT * FROM users WHERE phone LIKE '555-____'; -- 底线代表任意单一字符
-- IN 子句(比一堆 OR 干净)
SELECT * FROM users WHERE role IN ('admin', 'moderator');
-- 处理 NULL(⚠️ NULL 不等于 NULL)
SELECT * FROM users WHERE email IS NOT NULL;
SELECT * FROM products WHERE description IS NULL OR description = '';
-- BETWEEN(含头含尾)
SELECT * FROM orders WHERE created_at BETWEEN '2026-01-01' AND '2026-03-31';
JOINs:合并多张表
-- INNER JOIN:只保留两边都有匹配的行
SELECT u.name, o.total_amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- LEFT JOIN:左表全部保留,右表匹配不到就是 NULL
SELECT u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
-- 多重 JOIN
SELECT
u.name,
p.name AS product_name,
oi.quantity,
oi.unit_price
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE u.id = 123;
ORDER BY + LIMIT:排序与分页
-- 基本排序
SELECT * FROM products ORDER BY price ASC;
SELECT * FROM products ORDER BY created_at DESC;
-- 多栏位排序
SELECT * FROM users ORDER BY last_login DESC, name ASC;
-- 分页(很关键!)
-- 第 1 页(第 1-20 条):
SELECT * FROM products ORDER BY created_at DESC LIMIT 20 OFFSET 0;
-- 第 2 页(第 21-40 条):
SELECT * FROM products ORDER BY created_at DESC LIMIT 20 OFFSET 20;
-- 第 N 页:
SELECT * FROM products ORDER BY created_at DESC LIMIT 20 OFFSET ((N - 1) * 20);
聚合与分组(Aggregations & Grouping)
-- 基本聚合函数
SELECT
COUNT(*) AS total_orders,
SUM(total_amount) AS revenue,
AVG(total_amount) AS avg_order_value,
MIN(total_amount) AS min_order,
MAX(total_amount) AS max_order
FROM orders
WHERE status = 'completed';
-- GROUP BY 搭配 HAVING
SELECT
status,
COUNT(*) AS count,
SUM(total_amount) AS revenue
FROM orders
GROUP BY status
HAVING COUNT(*) > 10; -- 分组"之后"才过滤,跟 WHERE 不一样
📌 WHERE vs HAVING:WHERE 是分组前先过滤原始数据;HAVING 是分组、算完聚合值之后才过滤。要按聚合结果(比如"数量超过 10")筛选,就必须用 HAVING,不能用 WHERE。
常见进阶模式——每个分类抓 Top N:
SELECT
category,
product_name,
sales_count
FROM (
SELECT
c.name AS category,
p.name AS product_name,
SUM(oi.quantity) AS sales_count,
ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY SUM(oi.quantity) DESC) AS rank
FROM categories c
JOIN products p ON c.id = p.category_id
JOIN order_items oi ON p.id = oi.product_id
GROUP BY c.id, p.id
) ranked
WHERE rank <= 3; -- 每个分类只留销量前 3 的产品
这里用到窗口函数 ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)——先按分类分组、组内按销量排名,再用外层 WHERE 只留前 3 名,是"每组抓 Top N"这类需求的标准写法。
语法速查
| 任务 | 语法 |
|---|---|
| 取全部 | SELECT * FROM t |
| 过滤 | WHERE col = val |
| 模糊匹配 | WHERE col LIKE '%term' |
| 合并表 | FROM a JOIN b ON a.id = b.a_id |
| 分组聚合 | GROUP BY col HAVING condition |
| 排序 | ORDER BY col DESC |
| 分页 | LIMIT n OFFSET m |
适用版本
内容以 PostgreSQL 语法为主(ON CONFLICT、ILIKE、窗口函数等属于 PostgreSQL 常见写法),MySQL/SQL Server 等其他数据库部分语法可能略有差异,实际使用前建议对照所用数据库的官方文档确认。
常见错误
- ❌ 图方便直接
SELECT *——会浪费带宽、也会让数据库没办法用「仅索引扫描」这类优化,正式环境永远优先明确写出需要的栏位 - ❌ 以为
WHERE email = NULL能筛出 email 是空的行——SQL 里NULL不等于NULL,必须用IS NULL/IS NOT NULL来判断 - ❌ 想按聚合结果筛选却用了 WHERE——WHERE 是在分组前过滤原始数据,分组"之后"要过滤(比如按 COUNT 结果筛)必须用 HAVING
- ❌ 用一堆
OR拼多个条件——同一个栏位比对多个值时,IN (...)比一串OR干净很多 - 💡 需要"每个分类/群组抓前几名"这种需求,直接套用
ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)窗口函数的写法,比自己土法炼钢拼查询省事很多
Sources
Blog / Website:
- SQL Basics Every Developer Should Know (2026) — https://dev.to/armorbreak/sql-basics-every-developer-should-know-2026-2986