一、SQL 核心概述:关系型数据库的 "操作语言"
SQL(Structured Query Language)是关系型数据库的标准编程语言,核心作用是 存取、查询、更新和管理数据,支持从简单的数据增删改查到复杂的多表关联、数据统计、事务控制等场景。
本文将围绕 "基础操作→进阶特性→实战技巧" 三层结构展开,所有示例基于 MySQL 8.0 (兼容 PostgreSQL、SQL Server,差异会标注),沿用基础表 user_info 并新增关联表 order_info 用于进阶场景:
-- 基础表:用户信息表(沿用前文结构,补充完整约束)
CREATE TABLE user_info (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
age TINYINT CHECK (age > 0 AND age
gender ENUM('male', 'female', 'other'),
email VARCHAR(100) NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME ON UPDATE CURRENT_TIMESTAMP
);
-- 关联表:订单信息表(用于联表查询示例)
CREATE TABLE order_info (
order_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL, -- 关联 user_info.id
order_amount DECIMAL(10,2) NOT NULL,
order_status TINYINT NOT NULL COMMENT '0-待支付,1-已支付,2-已取消',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
-- 外键约束:确保 user_id 对应 user_info 中存在的用户
FOREIGN KEY (user_id) REFERENCES user_info(id) ON DELETE CASCADE
);
二、SQL 基础操作:CRUD 核心用法(回顾与补充)
CRUD 是 SQL 最基础的四大操作,覆盖单表数据全生命周期,前文已详细讲解核心用法,此处补充高频场景扩展:
1. 新增(Create):插入与批量插入优化
-- 扩展:插入查询结果(将查询到的数据直接插入表中)
INSERT INTO user_info (username, age, gender, email)
SELECT 'copy_user', age, gender, CONCAT('copy_', email)
FROM user_info
WHERE id = 1; -- 复制 id=1 的用户数据,修改用户名和邮箱
-- 批量插入优化:指定字段顺序,避免因表结构变更报错
INSERT INTO order_info (user_id, order_amount, order_status)
VALUES
(1, 99.90, 1),
(1, 199.50, 1),
(2, 299.00, 0);
2. 查询(Read):单表查询补充场景
-- 扩展1:条件查询 - 多值匹配优化(IN 与 EXISTS 对比)
-- 适合少量值匹配
SELECT username, email FROM user_info WHERE id IN (1,2,3);
-- 适合大量值匹配(性能更优)
SELECT username, email FROM user_info u
WHERE EXISTS (SELECT 1 FROM order_info o WHERE o.user_id = u.id);
-- 扩展2:日期范围查询(常用业务场景)
SELECT username, create_time
FROM user_info
WHERE create_time BETWEEN '2026-01-01 00:00:00' AND '2026-12-31 23:59:59';
-- 扩展3:模糊查询优化(前缀匹配可命中索引,后缀/中间匹配无法命中)
SELECT username FROM user_info WHERE username LIKE '张%'; -- 推荐(前缀匹配)
SELECT username FROM user_info WHERE username LIKE '%三'; -- 不推荐(后缀匹配)
3. 更新(Update):多表关联更新
-- 扩展:根据关联表数据更新目标表(例如:给有已支付订单的用户年龄+1)
UPDATE user_info u
JOIN order_info o ON u.id = o.user_id
SET u.age = u.age + 1
WHERE o.order_status = 1; -- 仅更新有已支付订单的用户
4. 删除(Delete):关联删除与条件优化
-- 扩展:多表关联删除(删除无订单的用户)
DELETE u FROM user_info u
LEFT JOIN order_info o ON u.id = o.user_id
WHERE o.user_id IS NULL; -- 左连接后筛选无关联订单的用户
三、SQL 进阶特性:从单表到复杂场景
1. 联表查询(JOIN):多表关联核心用法
联表查询是 SQL 进阶的核心,用于从多个关联表中提取数据,常用 JOIN 类型:
| JOIN 类型 | 说明 | 示例场景 |
|---|---|---|
INNER JOIN(内连接) |
只返回两表中匹配的记录 | 查询有订单的用户及订单信息 |
LEFT JOIN(左连接) |
返回左表所有记录,右表匹配不到则为 NULL | 查询所有用户及关联订单(无订单用户也显示) |
RIGHT JOIN(右连接) |
返回右表所有记录,左表匹配不到则为 NULL | 查询所有订单及关联用户(无用户的订单也显示) |
FULL JOIN(全连接) |
返回两表所有记录,匹配不到则为 NULL | MySQL 不直接支持,需用 UNION 模拟 |
实战示例:
-- 1. 内连接:查询有已支付订单的用户信息及订单金额
SELECT
u.username,
u.email,
o.order_id,
o.order_amount,
o.order_status
FROM user_info u
INNER JOIN order_info o ON u.id = o.user_id
WHERE o.order_status = 1;
-- 2. 左连接:查询所有用户及关联订单(无订单用户的订单字段为 NULL)
SELECT
u.username,
o.order_id,
IFNULL(o.order_amount, 0) AS order_amount -- 用 IFNULL 处理 NULL 值
FROM user_info u
LEFT JOIN order_info o ON u.id = o.user_id;
-- 3. 模拟全连接(MySQL 特有):查询所有用户和所有订单的关联关系
SELECT u.username, o.order_id FROM user_info u LEFT JOIN order_info o ON u.id = o.user_id
UNION
SELECT u.username, o.order_id FROM user_info u RIGHT JOIN order_info o ON u.id = o.user_id;
2. 子查询:嵌套查询逻辑
子查询是将一个查询结果作为另一个查询的条件或数据源,分为 相关子查询 (依赖外部查询)和 非相关子查询(独立执行):
-- 1. 非相关子查询:查询订单金额大于平均订单金额的订单
SELECT order_id, order_amount
FROM order_info
WHERE order_amount > (SELECT AVG(order_amount) FROM order_info); -- 子查询独立执行
-- 2. 相关子查询:查询每个用户的最大金额订单
SELECT
o.user_id,
o.order_id,
o.order_amount
FROM order_info o
WHERE o.order_amount = (
SELECT MAX(order_amount)
FROM order_info
WHERE user_id = o.user_id -- 子查询依赖外部查询的 user_id
);
-- 3. 子查询用于 IN 条件(查询有订单金额>200的用户)
SELECT username FROM user_info
WHERE id IN (SELECT DISTINCT user_id FROM order_info WHERE order_amount > 200);
3. 事务(Transaction):保证数据一致性
事务是一组不可分割的 SQL 操作,遵循 ACID 原则(原子性、一致性、隔离性、持久性),用于解决并发场景下的数据一致性问题(如转账、下单支付):
-- 事务示例:用户下单支付(扣减余额→创建订单→更新订单状态)
START TRANSACTION; -- 开启事务
-- 1. 假设存在余额表,扣减用户余额(示例语句)
UPDATE user_wallet SET balance = balance - 199.90 WHERE user_id = 1;
-- 2. 创建订单
INSERT INTO order_info (user_id, order_amount, order_status)
VALUES (1, 199.90, 1);
-- 3. 验证操作是否成功,成功则提交,失败则回滚
IF @@ROWCOUNT > 0 THEN
COMMIT; -- 提交事务(所有操作生效)
ELSE
ROLLBACK; -- 回滚事务(所有操作撤销)
END IF;
-- 简化写法:使用 TRY...CATCH(MySQL 8.0+ 支持)
BEGIN TRY
START TRANSACTION;
UPDATE user_wallet SET balance = balance - 199.90 WHERE user_id = 1;
INSERT INTO order_info (user_id, order_amount, order_status) VALUES (1, 199.90, 1);
COMMIT;
END TRY
BEGIN CATCH
ROLLBACK;
SELECT '事务执行失败:' + ERROR_MESSAGE() AS error_msg;
END CATCH;
4. 索引:提升查询性能
索引是数据库优化的核心,通过建立 "数据目录" 减少全表扫描,常用索引类型:主键索引、唯一索引、普通索引、联合索引:
-- 1. 创建普通索引(用于查询条件列)
CREATE INDEX idx_user_info_age ON user_info(age); -- 给 age 列建索引
CREATE INDEX idx_order_info_user_id ON order_info(user_id); -- 给关联列建索引
-- 2. 创建联合索引(适合多列查询条件,遵循"最左前缀原则")
CREATE INDEX idx_user_info_gender_age ON user_info(gender, age); -- 先匹配 gender,再匹配 age
-- 3. 查看索引使用情况(验证索引是否生效)
EXPLAIN SELECT username FROM user_info WHERE gender = 'female' AND age = 25; -- 会使用联合索引
-- 4. 删除无用索引(避免占用空间)
DROP INDEX idx_user_info_age ON user_info;
索引最佳实践:
-
给查询频繁的列(WHERE、JOIN、ORDER BY 后的列)建索引;
-
避免给更新频繁、数据重复率高的列建索引(如性别列,重复率高,索引效果差);
-
联合索引需按 "查询频率从高到低" 排列列顺序。
5. 函数与表达式:数据处理与格式化
SQL 内置丰富函数,用于数据计算、字符串处理、日期格式化等:
-- 1. 字符串函数
SELECT
CONCAT(username, '-', gender) AS user_label, -- 拼接字符串
LENGTH(username) AS name_length, -- 字符串长度
UPPER(email) AS email_upper -- 转大写
FROM user_info;
-- 2. 日期函数(常用业务场景)
SELECT
create_time,
DATE_FORMAT(create_time, '%Y-%m-%d') AS create_date, -- 格式化日期
DATEDIFF(NOW(), create_time) AS days_since_create -- 计算距今天数
FROM user_info;
-- 3. 聚合函数进阶(分组统计)
SELECT
DATE_FORMAT(o.create_time, '%Y-%m') AS order_month, -- 按年月分组
COUNT(o.order_id) AS order_count, -- 订单数
SUM(o.order_amount) AS total_amount, -- 总金额
AVG(o.order_amount) AS avg_amount -- 平均金额
FROM order_info o
GROUP BY order_month
HAVING total_amount > 1000; -- 筛选月总金额>1000的记录
四、SQL 实战场景:综合案例
需求:统计 2026 年每个月的用户下单情况,包括用户名、下单数、总金额、最大订单金额:
SELECT
u.username,
DATE_FORMAT(o.create_time, '%Y-%m') AS order_month,
COUNT(o.order_id) AS order_count,
SUM(o.order_amount) AS total_amount,
MAX(o.order_amount) AS max_amount
FROM user_info u
INNER JOIN order_info o ON u.id = o.user_id
WHERE o.create_time BETWEEN '2026-01-01' AND '2026-12-31'
GROUP BY u.username, order_month
ORDER BY order_month DESC, total_amount DESC;
五、SQL 避坑指南与最佳实践
1. 性能优化
-
避免
SELECT *,只查询需要的列; -
联表查询时,小表在前、大表在后(INNER JOIN 不影响,LEFT JOIN 需注意);
-
避免在 WHERE 子句中使用函数(如
DATE(create_time) = '2026-01-01'),会导致索引失效; -
批量操作优先用
INSERT ... VALUES (...)、UPDATE ... JOIN,减少 SQL 执行次数。
2. 兼容性处理
-
分页语法:MySQL 用
LIMIT,SQL Server 用TOP,PostgreSQL 用LIMIT/OFFSET; -
日期函数:MySQL 用
DATE_FORMAT,SQL Server 用FORMAT,PostgreSQL 用TO_CHAR; -
字符串拼接:MySQL 用
CONCAT,SQL Server 用+,PostgreSQL 用||。
参考资料:
-
MySQL 8.0 官方文档:https://dev.mysql.com/doc/refman/8.0/en/
-
SQL 核心语法规范:https://www.w3schools.com/sql/