SQL语法
基础语法
P1
# SQL语句(的关键字)不区分大小写
# 语句末尾的分号也是可选的
# 注释好像是使用"--"符号
# 代码好像可以随便换行
# 显示所有数据库
SHOW DATABASES;
# 选择数据库
USE RUNOOB;
# 显示所有的表
SHOW TABLES;
# 数据操作
# * 是通配符,表示选择所有的「列」
# 这里没有 WHERE 进行筛选,所以会输出所有行
SELECT * FROM Websites;
# 查询
SELECT column_name(s)
FROM TABLE_NAME
WHERE condition # 筛选「行」
ORDER BY column_name [ASC|DESC]
# 插入数据
INSERT INTO TABLE_NAME (column1, column2, ...)
VALUES (value1, value2, ...)
# 更新数据
UPDATE TABLE_NAME
SET column1 = value1, column2 = value2, ...
WHERE condition
# 删除(行)
DELETE FROM TABLE_NAME
WHERE condition
# 表操作
# 创建新TABLE
CREATE TABLE TABLE_NAME (
column1 data_type CONSTRAINT,
column2 data_type CONSTRAINT,
...
)
# 增加「列」
ALTER TABLE TABLE_NAME
ADD column_name data_type
# 删除「列」
ALTER TABLE TABLE_NAME
DROP COLUMN column_name
# 删除TABLE
DROP TABLE TABLE_NAME
P2
# IF
# 1.流程控制(不同数据库的语法都有些许不同)
# 2.用于根据条件返回不同的值
SELECT
name,
score,
IF(score >= 60, '及格', '不及格') AS RESULT
FROM students;
# 各种 JOIN 关系(聚合),对 SELECT 的结果使用别名
-- 插入数据(BQ语法)
INSERT INTO `ds.results` (id, name, score)
SELECT
student_data.id,
student_data.name,
score_data.score
FROM (
SELECT id, name
FROM `ds.students`
WHERE name LIKE 'G%'
) student_data -- alias for the subquery 一个别名
-- 不同的 JOIN 就是不同类型的集合操作,但本质上都是表的合并
LEFT OUTER JOIN ( -- LEFT OUTER JOIN == LEFT JOIN
SELECT id, score
FROM `ds.scores`
) score_data
ON (student_data.id = score_data.id) -- 两张表的拼接规则
WHERE score_data.score > 80; -- filter the final result (after join)
高级语法:聚合&分组
GROUP BY
-- aggregate_function是聚合函数(如 COUNT/SUM/AVG/MIN/MAX)对分组后的多行做汇总
SELECT column_name(s), aggregate_function(column_name)
FROM TABLE_NAME WHERE condition
GROUP BY column_name(s)
| id | customer_id | status | amount | order_date |
| 1 | C001 | paid | 100.0 | 2025-09-01 |
| 2 | C002 | paid | 200.0 | 2025-09-01 |
| 3 | C001 | refund | 50.0 | 2025-09-02 |
| 4 | C001 | paid | 120.0 | 2025-09-03 |
| 5 | C002 | paid | 80.0 | 2025-09-03 |
SELECT
customer_id,
COUNT(*) AS paid_orders,
SUM(amount) AS total_amount
FROM orders
WHERE STATUS = 'paid' -- 先筛选出已支付订单
GROUP BY customer_id; -- 再按客户分组
| customer_id | paid_orders | total_amount |
| C001 | 2 | 220.0 |
| C002 | 2 | 280.0 |
SELECT 里出现的非聚合列,必须出现在 GROUP BY(少数数据库的宽松模式除外,别依赖它)
WHERE 是分组前过滤;想对“分组后的结果”再过滤,用 HAVING
HAVING
-- WHERE:分组之前过滤“行”(不能用聚合函数)
-- HAVING:分组之后过滤“组”(可以用聚合函数,如 COUNT/SUM/AVG)
SELECT column_name(s), aggregate_function(column_name)
FROM TABLE_NAME
GROUP BY column_name(s)
HAVING condition
-- 只保留“订单数 ≥ 3”的客户
SELECT customer_id, COUNT(*) AS orders_cnt, SUM(amount) AS total_amount
FROM orders
WHERE STATUS = 'paid' -- 先把未支付的行排除(行级过滤)
GROUP BY customer_id
-- TODO: 这里能不能用 orders_cnt 这个变量?
HAVING COUNT(*) >= 3; -- 再按组过滤(组级过滤)
-- 总金额在 [500, 2000) 的客户
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
WHERE STATUS = 'paid'
GROUP BY customer_id
HAVING SUM(amount) >= 500 AND SUM(amount) < 2000;
-- 同时使用 WHERE 与 HAVING
-- 想统计 2025 年内、状态为已支付的订单里,有至少 2 天下过单的客户
SELECT customer_id, COUNT(DISTINCT DATE(order_date)) AS active_days
FROM orders
WHERE STATUS = 'paid'
AND order_date >= DATE '2025-01-01'
AND order_date < DATE '2026-01-01' -- 这些是行级条件,放 WHERE
GROUP BY customer_id
HAVING COUNT(DISTINCT DATE(order_date)) >= 2; -- 聚合条件,放 HAVING
-- 不带 GROUP BY 时的 HAVING
-- 有些场景要判断“整张表的聚合是否满足条件”,这时可以没有 GROUP BY
SELECT SUM(amount) AS revenue_2025
FROM orders
WHERE order_date >= DATE '2025-01-01' AND order_date < DATE '2026-01-01'
HAVING SUM(amount) > 100000; -- 只有在总营收 > 100000 时才返回一行,否则返回空集
性能:能放到 WHERE 的条件尽量放 WHERE,这样先减小数据量,再分组
聚合才用 HAVING:凡是和 COUNT/SUM/AVG/MIN/MAX 等聚合相关的过滤放 HAVING;纯列值过滤放 WHERE
别名在 HAVING:标准 SQL 不保证 HAVING 能用 SELECT 里的别名
JOIN
-- (内)连接(INNER JOIN)的基本写法:把两张表按某个“匹配条件”合在一起,只保留两边都能匹配到的行
-- 不带前缀时,JOIN 等同于 INNER JOIN
SELECT column_name(s)
FROM table_name1 JOIN table_name2
ON table_name1.column_name = table_name2.column_name -- 连接条件(两表的键如何对齐)
| customer_id | name |
| C001 | Alice |
| C002 | Bob |
| C003 | Carol |
| order_id | customer_id | amount |
| 1 | C001 | 100 |
| 2 | C001 | 50 |
| 3 | C002 | 80 |
SELECT
o.order_id,
c.name,
o.amount
FROM orders AS o
JOIN customers AS c
ON o.customer_id = c.customer_id;
| order_id | name | amount |
| 1 | Alice | 100 |
| 2 | Alice | 50 |
| 3 | Bob | 80 |
JOIN的种类
INNER JOIN(默认):两边都匹配才保留。
LEFT JOIN:保留左表全部,即使右边没匹配(右表列为 NULL)。
RIGHT JOIN:保留右表全部。
FULL OUTER JOIN:保留两边全部(部分数据库需用 UNION 实现)。
性能分析
EXPLAIN <SQL语句>;
EXPLAIN ANALYZE <SQL语句>;
-- 只看计划(估算,不执行)
EXPLAIN
SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC LIMIT 20;
-- 真正执行并给出实际耗时/行数(8.0.18+)
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC LIMIT 20;