PC TOOLS

EP01. “SQL 查询基础:SELECT、WHERE、JOIN”

首页 PC 工具 Calculating · SQL · EP01
约 13 分钟· #EP01#SQL
🔒 登录后可标记已读

这篇笔记讲开发者最常用到的 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 CONFLICTILIKE、窗口函数等属于 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:

  1. SQL Basics Every Developer Should Know (2026) — https://dev.to/armorbreak/sql-basics-every-developer-should-know-2026-2986