常用sql规范(持续更新)
SQL 语句开发规范:打造高效数据库操作的基石
📌 引言
在项目开发中,SQL 作为与数据库交互的核心语言,其编写质量直接影响系统性能、数据安全性和代码可维护性。缺乏规范的 SQL 语句可能导致查询效率低下、数据不一致、甚至引发安全漏洞(如 SQL 注入)。
本文档从命名、格式、语句类型、性能、事务、安全等维度,制定统一的 SQL 开发规范,旨在帮助开发人员编写高效、稳健、易维护的 SQL 代码,保障项目数据库层的稳定性与可扩展性。
一、一般原则(基础规范)
1.1 命名规范(核心:见名知意,统一风格)
命名是代码可读性的基础,需遵循以下规则:
| 对象类型 | 命名规则 | 示例 | 禁忌⚠️ |
|---|---|---|---|
| 数据库 | 小写字母,用_分隔,前缀为项目缩写 | edu_student_db(教育项目学生库) | 避免大写、特殊字符(如#)、保留字(如user) |
| 表 | 小写字母,_分隔,前缀为业务模块(如user_、order_),单数形式 | user_info(用户信息表)、order_detail(订单详情表) | 避免无意义命名(如table1)、中文 |
| 列 | 小写字母,_分隔,体现字段含义,避免冗余(如表名已含user,列无需user_id) | id(主键)、create_time(创建时间) | 避免使用name(需明确为user_name)、desc(保留字) |
| 索引 | 前缀区分类型:idx_(普通索引)、uniq_(唯一索引)、pk_(主键),后接字段 | idx_user_phone(用户手机号索引)、uniq_user_idcard(身份证唯一索引) | 避免索引名与表名重复 |
| 存储过程 / 函数 | 前缀区分类型:sp_(存储过程)、fn_(函数),后接业务逻辑 | sp_user_register(用户注册存储过程)、fn_calculate_order_amount(计算订单金额函数) | 避免无业务含义的命名 |
✅ 推荐:命名长度控制在 30 字符内,避免使用拼音(如
yonghu),优先使用英文(如
user)。
1.2 代码格式(核心:统一缩进,清晰易读)
格式混乱的 SQL 会大幅增加维护成本,需遵循以下规则:
1.2.1 关键字大小写
-
统一使用大写(如
SELECT、FROM、WHERE),区分关键字与业务字段,提升可读性。 -
❌ 错误示例:
select id, name from user_info where age > 18 -
✅ 正确示例:
SELECT id, name FROM user_info WHERE age > 18
1.2.2 缩进与换行
-
使用4 个空格缩进(避免 Tab,防止不同编辑器显示不一致)。
-
多表关联、子查询、条件判断时,按逻辑换行,每个子句单独占一行。
-
❌ 错误示例:
SELECT ``u.id``, o.order_no FROM user_info u JOIN order_info o ON ``u.id`` = o.user_id WHERE u.age > 18 AND o.create_time > '2024-01-01' -
✅ 正确示例:
SELECT
u.id,
o.order_no
FROM
user_info u
JOIN
order_info o
ON
u.id = o.user_id -- 关联条件单独换行
WHERE
u.age > 18
AND o.create_time > '2024-01-01'; -- 多条件分行,便于注释和修改
1.2.3 空格与符号
-
运算符(
=、>、<、+等)前后加 1 个空格,避免紧凑写法。- ❌
u.age>18→ ✅u.age > 18
- ❌
-
逗号(
,)后加 1 个空格,前不加空格。- ❌
u.id``,o.order_no→ ✅u.id``, o.order_no
- ❌
-
分号(
;)作为语句结束符,必须添加(避免多语句执行时歧义)。
1.2.4 注释规范
-
单行注释:使用
-- 注释内容(--后加 1 个空格),用于解释局部逻辑。 -
多行注释:使用
/* 注释内容 */,用于解释存储过程、函数的整体功能。 -
注释位置:关键逻辑(如复杂条件、关联规则)必须加注释,避免 “后人猜逻辑”。
示例:
-- 筛选18岁以上用户的2024年订单(业务需求:仅统计成年用户的有效订单)
SELECT
u.id,
o.order_no
FROM
user_info u
JOIN
order_info o
ON
u.id = o.user_id -- 关联用户与订单(用户ID为外键)
WHERE
u.age > 18
AND o.create_time > '2024-01-01'
AND o.status = 1; -- 1:订单已支付(状态枚举:0-待支付,1-已支付,2-已取消)
二、各类 SQL 语句规范(实操重点)
2.1 SELECT 语句(查询操作)
2.1.1 禁止使用SELECT *(核心原则)
-
❌ 错误:
SELECT * FROM user_info WHERE id = 1; -
✅ 正确:
SELECT id, user_name, phone FROM user_info WHERE id = 1;
原因:
-
减少无效数据传输(如无需查询的
password、remark字段),提升网络效率; -
避免表结构变更(如新增字段)导致代码异常(如 VO 映射失败);
-
明确查询字段,便于后续维护(无需查表结构即可知道返回内容)。
2.1.2 别名使用规范
-
表别名:使用1-2 个字母(基于表名首字母,如
user_info→u,order_info→o),避免长别名。 -
列别名:使用有意义的名称,避免中文,若需空格需用双引号(如
"user name")。
示例:
-- 表别名:u(user_info)、o(order_info);列别名:order_count(订单数量)
SELECT
u.user_name,
COUNT(o.id) AS order_count -- AS可省略,但建议保留(清晰区分字段与别名)
FROM
user_info u
LEFT JOIN
order_info o
ON
u.id = o.user_id
GROUP BY
u.user_name;
2.1.3 JOIN 操作规范
-
优先使用显式 JOIN(
INNER JOIN、LEFT JOIN),避免隐式 JOIN(逗号分隔表,条件写在WHERE)。-
❌ 隐式 JOIN:
SELECT * FROM user_info u, order_info o WHERE ``u.id`` = o.user_id; -
✅ 显式 JOIN:
SELECT * FROM user_info u INNER JOIN order_info o ON ``u.id`` = o.user_id;
-
-
左连接(
LEFT JOIN)需注意:右表字段可能为NULL,需用IFNULL或COALESCE处理。- 示例:
SELECT u.user_name, IFNULL(o.order_no, '无订单') AS order_no FROM user_info u LEFT JOIN order_info o ON ``u.id`` = o.user_id;
- 示例:
-
禁止使用RIGHT JOIN(逻辑易混淆,可用
LEFT JOIN反向关联替代)。
2.1.4 子查询规范
-
子查询需用括号包裹,且优先使用
EXISTS替代IN(大数据量下性能更优)。-
❌
SELECT * FROM user_info WHERE id IN (SELECT user_id FROM order_info WHERE status = 1); -
✅
SELECT * FROM user_info u WHERE EXISTS (SELECT 1 FROM order_info o WHERE o.user_id = u.id AND o.status = 1);
-
-
子查询嵌套不超过2 层(超过 2 层需拆分为 JOIN 或分步查询,避免性能损耗)。
-
❌ 3 层嵌套:
SELECT * FROM a WHERE id IN (SELECT a_id FROM b WHERE b_id IN (SELECT id FROM c WHERE status = 1)); -
✅ 拆分 JOIN:
SELECT a.* FROM a JOIN b ON a.id = b.a_id JOIN c ON b.b_id = c.id WHERE c.status = 1;
-
2.2 INSERT 语句(插入操作)
2.2.1 必须指定列名
-
❌ 错误:
INSERT INTO user_info VALUES (1, '张三', '13800138000');(表结构变更后必错) -
✅ 正确:
INSERT INTO user_info (id, user_name, phone) VALUES (1, '张三', '13800138000');
2.2.2 批量插入优化
- 单条
INSERT语句批量插入(避免循环执行多条INSERT,减少数据库连接开销)。
INSERT INTO user_info (id, user_name, phone)
VALUES
(1, '张三', '13800138000'),
(2, '李四', '13900139000'),
(3, '王五', '13700137000'); -- 批量插入3条数据,逗号分隔
- 示例(MySQL):
- 批量插入条数控制在1000 条以内(避免单条语句过长,导致执行超时)。
2.2.3 特殊值处理
-
日期时间:使用标准格式(如
'2024-05-20 14:30:00'),避免依赖数据库默认格式。 -
空值:明确使用
NULL(而非空字符串'',除非业务允许),如INSERT INTO user_info (id, email) VALUES (1, NULL);
2.3 UPDATE 语句(更新操作)
2.3.1 必须加WHERE条件(重中之重)
-
⚠️ 严禁无
WHERE的更新(会导致全表数据被修改)! -
❌ 错误:
UPDATE user_info SET phone = '13800138000'; -
✅ 正确:
UPDATE user_info SET phone = '13800138000' WHERE id = 1;
2.3.2 条件字段优先用索引
-
更新条件字段(如
WHERE id = 1中的id)需有索引,避免全表扫描(尤其大表)。 -
示例:
UPDATE order_info SET status = 2 WHERE order_no = '20240520001';(order_no需建索引)
2.3.3 避免更新大表全量数据
- 若需更新大表(10 万 + 数据),需分批更新(如按
id分段),避免锁表导致业务阻塞。
-- 第1批:id < 10000
UPDATE order_info SET status = 2 WHERE status = 1 AND id < 10000;
-- 第2批:10000 ≤ id < 20000
UPDATE order_info SET status = 2 WHERE status = 1 AND id BETWEEN 10000 AND 19999;
- 示例(分批更新状态):
2.4 DELETE 语句(删除操作)
2.4.1 必须加WHERE条件(同 UPDATE)
-
⚠️ 严禁无
WHERE的删除(会导致全表数据丢失,且难以恢复)! -
❌ 错误:
DELETE FROM user_info; -
✅ 正确:
DELETE FROM user_info WHERE id = 1;
2.4.2 优先用LIMIT限制删除条数
-
即使有
WHERE条件,也建议用LIMIT避免误删过多数据(尤其条件判断错误时)。- 示例:
DELETE FROM order_info WHERE create_time < '2023-01-01' LIMIT 1000;(每次删 1000 条,分批执行)
- 示例:
2.4.3 避免直接删除(推荐逻辑删除)
-
业务数据优先使用逻辑删除(新增
is_deleted字段,0- 未删除,1- 已删除),而非物理删除(DELETE)。- 示例:
UPDATE user_info SET is_deleted = 1 WHERE id = 1;(逻辑删除)
- 示例:
-
物理删除仅用于临时表、日志表等非核心数据,且删除前需备份。
三、性能优化规范(核心目标:提升查询效率)
3.1 索引使用规范
3.1.1 索引创建原则
-
优先在查询条件字段(
WHERE)、关联字段(JOIN ON)、排序字段(ORDER BY)上创建索引。 -
避免过度索引:一张表索引不超过5 个(索引会加速查询,但会减慢
INSERT/UPDATE/DELETE,因需维护索引结构)。 -
联合索引遵循 “最左前缀原则”:如
idx_age_name (age, name),仅WHERE age = 18或WHERE age = 18 AND name = '张三'会命中索引,WHERE name = '张三'不会命中。
3.1.2 禁止索引失效操作
以下操作会导致索引失效,需避免:
-
索引字段用函数:
WHERE SUBSTR(phone, 1, 3) = '138'(改用WHERE phone LIKE '138%'); -
索引字段类型转换:
WHERE id = '1'(id为 int 类型,应写WHERE id = 1); -
用
NOT/!=/<>:WHERE age != 18(改用WHERE age < 18 OR age > 18,若需全量过滤,建议业务层处理); -
用
OR连接非索引字段:WHERE age = 18 OR name = '张三'(name无索引时,索引失效)。
3.2 分页查询优化
3.2.1 避免OFFSET过大
-
传统分页(
LIMIT offset, size)在offset过大时(如LIMIT 10000, 10),会扫描前 10010 条数据,性能差。 -
✅ 优化方案:用主键分段(适用于主键自增):
-- 第1页:id < 10
SELECT id, user_name FROM user_info WHERE is_deleted = 0 ORDER BY id LIMIT 10;
-- 第2页:id > 10 AND id < 20(以上一页最大id为条件)
SELECT id, user_name FROM user_info WHERE is_deleted = 0 AND id > 10 ORDER BY id LIMIT 10;
3.2.2 覆盖索引分页
- 若查询字段均在索引中(覆盖索引),无需回表查询,性能大幅提升。
SELECT id, is_deleted FROM user_info WHERE is_deleted = 0 ORDER BY id LIMIT 10000, 10;
- 示例:
idx_is_deleted_id (is_deleted, id)为覆盖索引,查询id和is_deleted时直接用索引:
3.3 其他性能优化点
-
禁止使用
SELECT DISTINCT(去重会增加排序开销,优先在业务层去重); -
避免
ORDER BY非索引字段(大表会导致全表排序,耗时久); -
大表查询用
EXPLAIN分析执行计划(查看是否命中索引、是否全表扫描)。
- 示例:
EXPLAIN SELECT * FROM user_info WHERE age > 18;(分析该查询是否命中age索引)。
更多推荐

所有评论(0)