怎么把 SQL 的增删改查写成“稳、准、可维护”的

怎么把 SQL 的增删改查写成"稳、准、可维护"的

写 SQL 的目标从来不只是"跑出来结果",而是:

  • 不踩 NULL 的坑
  • 不发生"手滑全表更新/删除"的事故
  • 能做 统计、分组、分页
  • 需要时用函数把数据处理干净

下面按常用的节奏走一遍。


1)Create:INSERT 新增

1.1 全列插入(不写列名)

sql 复制代码
INSERT INTO exam VALUES (1, '张三', 90, 95, 88);

只有在"确定表结构顺序稳定、列数也不变"的情况下才这么写,否则更推荐写列名。

1.2 指定列插入(推荐)

sql 复制代码
INSERT INTO exam (id, name, chinese) VALUES (2, '李四', 92);

好处是:列顺序不怕变,也能只给部分列。

1.3 多行插入

sql 复制代码
INSERT INTO exam (id, name, chinese) VALUES
(3, '王五', 85),
(4, '赵六', 78);

2)Retrieve:SELECT 查询(最常用的一套)

2.1 查全表 / 查指定列

sql 复制代码
SELECT * FROM exam;
sql 复制代码
SELECT id, name, chinese FROM exam;

在真实项目里不可以无脑 SELECT *,字段多、数据大时会拖慢一切。

2.2 SELECT 里写表达式(常量、运算、总分)

sql 复制代码
SELECT id, name, chinese + math + english AS total_score
FROM exam;

一个会牢记的坑:如果 mathenglish 有 NULL,总分会变成 NULL(因为 NULL 参与运算结果仍是 NULL)。

2.3 DISTINCT 去重("整行"去重)

sql 复制代码
SELECT DISTINCT name, chinese FROM exam;

用 DISTINCT 时会明确:它不是"按某一列去重",而是"查询结果里所有列都相同才算重复"。


3)WHERE 条件过滤:比较、范围、集合、NULL、模糊

3.1 比较运算符 + NULL 安全等于 <=>

sql 复制代码
SELECT * FROM exam WHERE chinese >= 90;

如果要判断 NULL,我不会写 = NULL,而是:

sql 复制代码
SELECT * FROM exam WHERE english IS NULL;

如果真的需要"NULL 也能比较相等",我会用 <=>

sql 复制代码
SELECT * FROM exam WHERE english <=> NULL;

3.2 BETWEEN / IN

sql 复制代码
SELECT * FROM exam WHERE chinese BETWEEN 80 AND 90;
sql 复制代码
SELECT * FROM exam WHERE id IN (1, 3, 5);

3.3 LIKE 模糊匹配(% 和 _)

sql 复制代码
SELECT * FROM exam WHERE name LIKE '张%';
sql 复制代码
SELECT * FROM exam WHERE name LIKE '_三';

3.4 AND/OR 优先级(强制加括号)

sql 复制代码
SELECT * FROM exam
WHERE (chinese >= 90 AND math >= 90) OR english >= 95;

因为 AND 优先级高于 OR,宁愿多写括号,也不赌"记得优先级"。

3.5 一个硬规则:WHERE 里不能直接用别名

下面这种很多人会写错:

sql 复制代码
-- ❌(别名 total_score 在 WHERE 阶段不可用)
SELECT id, name, chinese + math + english AS total_score
FROM exam
WHERE total_score >= 270;

改成:

sql 复制代码
SELECT id, name, chinese + math + english AS total_score
FROM exam
WHERE (chinese + math + english) >= 270;

4)ORDER BY 排序:多列、表达式、别名、NULL 排序

4.1 按总分降序(表达式排序)

sql 复制代码
SELECT id, name, chinese + math + english AS total_score
FROM exam
ORDER BY total_score DESC;

用别名排序:ORDER BY 阶段是能识别别名的。

4.2 多列排序(先总分,再语文)

sql 复制代码
SELECT id, name, chinese + math + english AS total_score
FROM exam
ORDER BY total_score DESC, chinese DESC;

记住:NULL 被认为比任何值都小------升序时靠前,降序时靠后。


5)LIMIT 分页:最常用的写法

这种,语义清晰:

sql 复制代码
SELECT id, name, chinese
FROM exam
ORDER BY id
LIMIT 10 OFFSET 20;

含义是:从第 21 条开始取 10 条(OFFSET 从 0 开始计)。


6)Update:UPDATE 修改(单列/多列 + 安全写法)

6.1 基于原值递增

sql 复制代码
UPDATE exam
SET math = math + 10
WHERE id = 1;

不写 math += 10,SQL 不认这种语法。

6.2 一次改多列

sql 复制代码
UPDATE exam
SET chinese = 95, english = 96
WHERE name = '张三';

UPDATE 不写 WHERE 等于自爆


7)Delete:DELETE 删除(同样支持排序与限制)

7.1 删除某个条件下的记录

sql 复制代码
DELETE FROM exam WHERE id = 4;

7.2 只删"排序后的前 N 条"

sql 复制代码
DELETE FROM exam
ORDER BY id DESC
LIMIT 3;

同样铁律:DELETE 不写 WHERE,基本就是事故预备役


8)TRUNCATE:清空整张表(快,但很"硬")

sql 复制代码
TRUNCATE TABLE exam;

把 TRUNCATE 当成"重置按钮":

  • 只能整表清空
  • 通常更快
  • 不走逐行删除逻辑,很多场景下不可回滚
  • 会重置自增(AUTO_INCREMENT)

所以只在非常确定的时候用。


9)INSERT ... SELECT:把查询结果落到新表(去重落表特别好用)

一个很常用的"去重落表"套路:

sql 复制代码
CREATE TABLE exam_new LIKE exam;

INSERT INTO exam_new
SELECT DISTINCT * FROM exam;

这样我以后查 exam_new 就不需要每次都 DISTINCT 了(查询成本更低、逻辑更干净)。


10)聚合函数:COUNT / SUM / AVG / MAX / MIN(最常用的统计)

10.1 统计行数、统计非 NULL 数量

sql 复制代码
SELECT COUNT(*) AS total_rows FROM exam;
sql 复制代码
SELECT COUNT(english) AS english_not_null FROM exam;

牢记:COUNT(col) 不数 NULL。

10.2 求和/平均/最大最小

sql 复制代码
SELECT
  SUM(chinese) AS sum_chinese,
  AVG(chinese) AS avg_chinese,
  MAX(chinese) AS max_chinese,
  MIN(chinese) AS min_chinese
FROM exam;

11)GROUP BY 分组 + HAVING 分组过滤(统计分析的核心)

11.1 按部门统计人数(示例:emp 表)

sql 复制代码
SELECT dept_id, COUNT(*) AS cnt
FROM emp
GROUP BY dept_id;

11.2 HAVING:过滤"分组后的结果"

比如想找"平均工资大于 1500 的部门":

sql 复制代码
SELECT dept_id, AVG(salary) AS avg_salary
FROM emp
GROUP BY dept_id
HAVING AVG(salary) > 1500;

区分 WHERE 和 HAVING 的方式很简单:

  • WHERE 过滤的是"原始行"
  • HAVING 过滤的是"聚合后的组"

12)常用内置函数:日期、字符串、数学、其他(直接上手的那种)

12.1 日期函数

取当前时间:

sql 复制代码
SELECT NOW();

取日期部分:

sql 复制代码
SELECT DATE(NOW());

日期加减(例如:当前日期加 7 天):

sql 复制代码
SELECT DATE_ADD(CURDATE(), INTERVAL 7 DAY);

算日期差(天):

sql 复制代码
SELECT DATEDIFF('2025-01-10', '2025-01-01') AS diff_days;

12.2 字符串函数

长度(字符长度 vs 字节长度):

sql 复制代码
SELECT CHAR_LENGTH('重邮'), LENGTH('重邮');

拼接:

sql 复制代码
SELECT CONCAT(name, '-', id) AS tag FROM exam;

截取与替换:

sql 复制代码
SELECT SUBSTRING('abcdef', 2, 3);
sql 复制代码
SELECT REPLACE('hello world', 'world', 'SQL');

去两端空格:

sql 复制代码
SELECT TRIM('   sql  ');

12.3 数学函数

绝对值/取整/四舍五入:

sql 复制代码
SELECT ABS(-10), CEIL(3.14), FLOOR(3.99), ROUND(3.14159, 2);

随机数:

sql 复制代码
SELECT RAND();

12.4 其他常用(排障/处理 NULL 经常用)

当前库/当前用户/版本:

sql 复制代码
SELECT DATABASE(), USER(), VERSION();

NULL 兜底(总分避免 NULL 传染):

sql 复制代码
SELECT id, name,
       IFNULL(chinese, 0) + IFNULL(math, 0) + IFNULL(english, 0) AS total_score
FROM exam;

"SQL 防翻车三原则"

1)UPDATE/DELETE:先写 WHERE,再写 SET/删法,再执行

2)AND/OR 混用:一律上括号,不靠记忆

3)遇到 NULL:判断用 IS NULL,兜底用 IFNULL,比较需要时用 <=>

相关推荐
南汐以墨2 小时前
UI自动化测试指南(二):常用方法
java·测试工具
小蜗牛的路2 小时前
MySQL-连接很慢,10秒钟才有响应、Temporary failure in name resolution
数据库·mysql
l1t2 小时前
DeepSeek总结的SQL 数独:约束编程
数据库·sql
菩提祖师_2 小时前
基于单片机指纹密码门禁的设计与实现
数据库·单片机·嵌入式硬件
萧曵 丶2 小时前
Java 泛型详解
java·开发语言·泛型
在风中的意志2 小时前
[数据库SQL] [leetcode-175] 175. 组合两个表
数据库·sql·leetcode
dingchengyu2 小时前
Oracle 数据库中递归查询
数据库·oracle
在风中的意志2 小时前
[数据库SQL] [leetcode-183] 183. 从不订购的客户
数据库·sql
独断万古他化2 小时前
【Spring Web MVC 入门实战】实战三部曲由易到难:加法计算器 + 用户登录 + 留言板全流程实现
java·后端·spring·mvc