SQL 用法详解:从基础操作到进阶实战的全场景指南

一、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 用 ||

参考资料:

相关推荐
ew452182 小时前
【SQL】DISTINCT 与 GROUP BY 核心区别及常见误区、问题全梳理
sql·mysql
NCU_wander2 小时前
操作系统/数据库和业务应用/中间件/硬件之间的关系
数据库·中间件
Navicat中国2 小时前
如何从0到1完成函数设计 | Navicat 教程
数据库·函数·navicat
jnrjian2 小时前
Oracle tablespace 对象迁移
数据库·oracle
chushiyunen2 小时前
人工智能-function calling(函数调用)
数据库·ai编程
阿里云大数据AI技术2 小时前
EMR Serverless Spark 携手 PAI/百炼,开启“SQL 即 AI”的新篇章
sql·阿里云·spark·serverless·pai
TDengine (老段)2 小时前
TDengine IDMP 0-阅读指南
大数据·数据库·人工智能·时序数据库·tdengine·涛思数据
2501_945424802 小时前
机器学习与人工智能
jvm·数据库·python
Liu628882 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python