尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

SQL增删改查(CRUD)入门:从基础语法到实战避坑指南

SQL增删改查(CRUD)入门:从基础语法到实战避坑指南 1. 项目概述从“增删改查”开始你的数据世界之旅如果你刚接触数据库或者正在为课程设计、项目开发而头疼那么“增删改查”这四个字你一定不陌生。这听起来像是某种神秘的咒语但实际上它是我们与数据库这座数据仓库对话的最基本、最核心的语言。无论是管理一个简单的通讯录还是支撑一个千万级用户的电商平台其背后最频繁、最基础的操作都离不开这四件事增加Create、查询Read、更新Update、删除Delete合称CRUD。今天我们不谈高深的分布式架构也不聊复杂的性能调优就从一个一线开发者的视角掰开揉碎了讲讲SQL的这四条基本语句。我的目标是让你看完之后不仅能写出正确的语句更能理解为什么这么写以及在实际操作中会遇到哪些“坑”从而真正迈出掌控数据的第一步。2. 核心需求解析为什么“增删改查”是基石在深入语法之前我们得先明白为什么几乎所有数据库教程、面试乃至日常开发都从“增删改查”开始。这并非偶然而是由数据的生命周期和业务的基本逻辑决定的。2.1 数据的完整生命周期管理想象一下你正在开发一个博客系统。一个用户从注册到注销一篇文章从草稿到发布再到删除这个完整的过程就是数据生命周期的缩影。增Create用户注册时向users表插入一条新记录用户撰写文章时向articles表插入一条新记录。这是数据的“诞生”。查Read用户浏览首页时你需要从articles表查询最新的文章列表用户登录时需要根据用户名和密码查询users表进行验证。这是数据的“展示”与“使用”。改Update用户修改个人头像或昵称需要更新users表中的对应记录文章发布后作者修改了内容需要更新articles表。这是数据的“成长”与“变更”。删Delete用户注销账号需要在符合业务规则的前提下删除users表中的记录管理员清理违规评论需要删除comments表中的记录。这是数据的“终结”。可以看到任何业务功能最终都映射为对这四种操作的组合调用。它们是构建所有复杂数据交互的原子操作。2.2 业务逻辑的底层支撑更复杂的功能如统计、报表、关联推荐其底层依然是高效的“查”。一个复杂的多表关联查询可能涉及JOIN、GROUP BY、HAVING等高级语法但其核心目的仍然是“读取并组合数据”。而事务Transaction的概念也常常是为了保证一组“增删改”操作的原子性。因此熟练掌握“增删改查”是理解更高级数据库概念和编写高效业务代码的绝对前提。注意很多新手会急于学习“高级”特性如存储过程、触发器却忽略了基本功。这就像还没学会走路就想跑在实际开发中清晰、正确、高效的CRUD语句能解决90%以上的数据操作问题也是进行SQL优化、排查慢查询的基础。3. 环境准备与工具选择工欲善其事必先利其器。在学习SQL语句前你需要一个可以实践的环境。这里我提供几个最主流、对新手友好的选择方案。3.1 数据库选择MySQL / PostgreSQL对于初学者和个人项目我首推MySQL或PostgreSQL。两者都是开源、流行、社区活跃的关系型数据库。MySQL更“大众化”安装简单资料极多是很多Web应用如WordPress的默认选择。它的语法相对宽松一些。PostgreSQL更“学院派”标准SQL兼容性更好功能更强大严谨如对事务、复杂数据类型的支持。近年来在开源社区势头很猛。你可以任选其一。它们的核心CRUD语法几乎完全一致本文的示例将以兼容性最高的标准SQL为主并注明可能的差异。3.2 安装与图形化工具本地安装去官网下载安装包是最直接的方式。对于MySQL你可以下载包含MySQL Server和Workbench图形界面的安装包。对于PostgreSQL安装时会自带pgAdmin图形工具。使用Docker如果你熟悉Docker这是最干净、便捷的方式。一条命令即可启动一个数据库实例无需担心污染本地环境。# 启动一个MySQL容器 docker run --name some-mysql -e MYSQL_ROOT_PASSWORDmy-secret-pw -d mysql:latest # 启动一个PostgreSQL容器 docker run --name some-postgres -e POSTGRES_PASSWORDmy-secret-pw -d postgres:latest图形化管理工具GUI强烈建议使用。它让你能直观地看到表结构、数据并方便地执行和调试SQL语句。DBeaver免费、开源、功能强大支持几乎所有数据库。这是我个人最推荐的工具一劳永逸。Navicat商业软件体验很好但需要付费。MySQL Workbench / pgAdmin数据库官方工具免费但功能相对专注。3.3 创建练习数据库和表安装好数据库和工具后连接上你的数据库服务器然后执行以下SQL语句创建一个用于练习的简单表-- 创建一个名为 practice_db 的数据库如果不存在 CREATE DATABASE IF NOT EXISTS practice_db; USE practice_db; -- MySQL中使用此语句切换数据库 -- PostgreSQL中应使用\c practice_db; (在psql命令行中) 或在连接时指定。 -- 创建一个 students 学生表 CREATE TABLE students ( id INT PRIMARY KEY AUTO_INCREMENT, -- MySQL的自增语法 -- PostgreSQL中为id SERIAL PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT, email VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );这张表有id主键自增、name姓名非空、age年龄、email邮箱和created_at创建时间默认当前时间五个字段足够我们演示所有基础操作。4. “增”INSERT向数据库添加新记录INSERT语句负责将新数据放入表中。这是数据产生的源头。4.1 基本语法与示例最常用的写法是指定列名并插入对应的值。这明确了数据对应关系即使表结构后续增加新列语句也依然有效。INSERT INTO students (name, age, email) VALUES (张三, 20, zhangsanexample.com);执行后students表里就会多出一条记录。id字段由于是AUTO_INCREMENT自增数据库会自动生成一个唯一值比如1。created_at字段因为有默认值CURRENT_TIMESTAMP也会自动填入当前时间。4.2 插入多行数据与省略列名一次插入多行数据效率远高于多次执行单行插入。INSERT INTO students (name, age, email) VALUES (李四, 22, lisiexample.com), (王五, 19, wangwuexample.com), (赵六, 21, zhaoliuexample.com);你也可以省略列名但这时必须为表中的每一列都提供值并且顺序必须与表定义完全一致。这种方法非常不推荐因为一旦表结构变更这条SQL很可能就会出错。-- 不推荐省略列名需按(id, name, age, email, created_at)顺序提供所有值 INSERT INTO students VALUES (NULL, 孙七, 23, sunqiexample.com, NOW()); -- 这里id传NULL数据库会自动分配自增值。4.3 从其他表插入数据INSERT还可以与SELECT结合将一个查询的结果直接插入到另一张表中。这在数据迁移或备份时非常有用。-- 假设有一张新生表new_students结构相同 INSERT INTO students (name, age, email) SELECT name, age, email FROM new_students WHERE age 18;实操心得始终指定列名这是一个必须养成的好习惯。它能提高代码可读性避免因表结构变更导致的错误并且在表中有大量允许为NULL的列时能精确控制插入的数据。处理唯一性冲突如果插入的数据违反了主键或唯一约束会报错。可以使用INSERT IGNOREMySQL或ON CONFLICT DO NOTHINGPostgreSQL来忽略重复插入或者使用ON DUPLICATE KEY UPDATEMySQL或ON CONFLICT DO UPDATEPostgreSQL来更新已存在的记录。这是实现“存在则更新不存在则插入”的常用技巧。5. “查”SELECT从数据库读取数据SELECT是使用频率最高、也最复杂的语句。它决定了你能从数据库中看到什么。5.1 最基本的查询SELECT * FROMSELECT * FROM table_name;会返回指定表中的所有行和所有列。*是通配符代表所有列。-- 查询students表中的所有数据 SELECT * FROM students;虽然简单但在生产环境中要慎用SELECT *尤其是当表有很多列或包含大文本字段时它会消耗不必要的网络和计算资源。最佳实践是始终指定需要的列。5.2 选择特定列与列别名指定你真正关心的列查询更高效结果也更清晰。SELECT name, age FROM students;你可以使用AS关键字给列或表起一个别名这在列名复杂或计算字段时特别有用。SELECT name AS 学生姓名, age AS 年龄, CONCAT(name, (, age, 岁)) AS 描述信息 -- 一个计算字段的例子 FROM students;5.3 使用WHERE子句进行条件过滤WHERE子句是SELECT的灵魂用于筛选出符合条件的行。它支持丰富的运算符。比较运算符:,或!不等于,,,SELECT * FROM students WHERE age 20; SELECT * FROM students WHERE name 张三;逻辑运算符:AND,OR,NOTSELECT * FROM students WHERE age 18 AND age 22; SELECT * FROM students WHERE name LIKE 张% OR email LIKE %example.com;范围与集合:BETWEEN ... AND ...,IN (...)SELECT * FROM students WHERE age BETWEEN 19 AND 21; -- 包含边界 SELECT * FROM students WHERE name IN (张三, 李四, 王五);模糊匹配:LIKE配合通配符%任意多个字符和_一个字符SELECT * FROM students WHERE name LIKE 张%; -- 姓张的 SELECT * FROM students WHERE email LIKE %gmail.com; -- Gmail邮箱空值判断:IS NULL,IS NOT NULLSELECT * FROM students WHERE email IS NULL; -- 查找未填写邮箱的学生5.4 对结果排序ORDER BY使用ORDER BY子句可以对查询结果进行排序。ASC为升序默认DESC为降序。-- 按年龄升序排列 SELECT * FROM students ORDER BY age ASC; -- 按年龄降序排列年龄相同的按姓名升序排列 SELECT * FROM students ORDER BY age DESC, name ASC;5.5 限制返回结果数量LIMITLIMIT子句用于限制返回的行数常用于分页或只查看前几条记录。-- 查看年龄最大的3个学生 SELECT * FROM students ORDER BY age DESC LIMIT 3; -- 分页查询每页10条查看第2页跳过前10条取接下来的10条 -- MySQL语法 SELECT * FROM students ORDER BY id LIMIT 10 OFFSET 10; -- 或简写为 LIMIT 10, 10注意LIMIT是MySQL、PostgreSQL等数据库的语法。SQL Server使用TOP关键字而Oracle使用ROWNUM。这是不同数据库方言的一个典型差异。5.6 聚合函数与分组统计GROUP BY聚合函数对一组值执行计算并返回单个值。常与GROUP BY子句一起使用用于分组统计。 常用聚合函数COUNT()计数SUM()求和AVG()平均值MAX()最大值MIN()最小值。-- 统计学生总人数 SELECT COUNT(*) AS 总人数 FROM students; -- 统计每个年龄段的学生人数 SELECT age, COUNT(*) AS 人数 FROM students GROUP BY age ORDER BY age; -- 计算所有学生的平均年龄 SELECT AVG(age) AS 平均年龄 FROM students;使用GROUP BY时SELECT子句中只能出现聚合函数和GROUP BY后面的列。如果需要基于聚合结果进行过滤必须使用HAVING子句而不是WHERE。-- 找出人数超过1人的年龄段 SELECT age, COUNT(*) AS 人数 FROM students GROUP BY age HAVING COUNT(*) 1;WHERE和HAVING的区别WHERE在分组前过滤行HAVING在分组后过滤组。6. “改”UPDATE更新已有记录当数据发生变化时我们需要使用UPDATE语句来修改表中已有的记录。6.1 基本语法与示例UPDATE语句需要指定要更新的表、要设置的新值以及通过WHERE子句精确指定要更新哪些行。-- 将张三的年龄更新为21岁 UPDATE students SET age 21 WHERE name 张三;SET子句用于指定要修改的列和新的值。可以同时更新多列用逗号分隔。UPDATE students SET age 22, email new_emailexample.com WHERE id 1;6.2 基于现有值的更新新值可以是一个表达式基于当前值进行计算。-- 将所有学生的年龄增加1岁 UPDATE students SET age age 1; -- 注意这条语句没有WHERE子句会更新表中的所有行务必小心。6.3 UPDATE的“危险”与WHERE子句的重要性这是SQL操作中最容易导致事故的地方之一。忘记写WHERE子句或者WHERE条件写得太宽泛会导致大量数据被意外更新。-- 灾难性语句更新了所有学生的邮箱 UPDATE students SET email oopsexample.com; -- 没有WHERE条件核心禁忌在执行任何UPDATE或DELETE语句前务必先将其写成SELECT语句进行验证。 例如你想更新id为5的学生应该先执行SELECT * FROM students WHERE id 5; -- 确认这条记录是你想改的确认无误后再将SELECT *替换为UPDATE ... SET ...。 另外在重要操作前开启数据库事务BEGIN;如果发现改错了可以立即回滚ROLLBACK;这是另一个重要的安全习惯。7. “删”DELETE从数据库移除记录DELETE语句用于从表中删除记录。它比UPDATE更“危险”因为数据一旦删除恢复起来非常困难虽然有些数据库有闪回功能但不能依赖。7.1 基本语法与示例和UPDATE一样DELETE必须配合WHERE子句使用以精确指定要删除的行。-- 删除姓名为‘赵六’的学生记录 DELETE FROM students WHERE name 赵六;7.2 清空表DELETE vs. TRUNCATE如果想删除表中的所有数据有两种方式DELETE FROM students;不带WHERE条件这是一条一行一行删除记录的SQL语句。它会触发可能存在的删除触发器。由于是逐行操作对于大表来说非常慢。在支持事务的数据库中这个操作可以被回滚。TRUNCATE TABLE students;这是一个DDL数据定义语言命令直接清空并重置整个表。速度极快因为它不记录逐行的删除日志。通常不触发删除触发器。在大多数数据库中这个操作无法被回滚。同时会重置表的自增计数器如MySQL的AUTO_INCREMENT。如何选择如果只是需要快速清空一个大表的所有数据且不需要触发器和事务回滚用TRUNCATE。如果需要精确控制删除哪些行或者需要触发器工作用DELETE并务必带上WHERE。重要安全规范永远对DELETE使用WHERE和UPDATE一样养成先写SELECT验证的习惯。软删除而非物理删除在实际业务系统中为了数据安全和审计需求极少直接物理删除数据。更常见的做法是增加一个is_deleted字段或deleted_at时间戳执行“删除”时只是更新这个标记位即一个UPDATE操作。查询时默认加上WHERE is_deleted 0。这称为“软删除”。权限控制在生产数据库应将DELETE和TRUNCATE权限严格限制给少数管理员。8. 常见问题与排查技巧实录即使掌握了语法在实际编写和运行SQL时你依然会遇到各种问题。下面是我总结的一些典型场景和解决方法。8.1 语法错误与拼写错误这是新手最常见的问题。数据库会给出错误信息但有时不太直观。错误示例SELECT nmae FROM students;列名拼写错误排查仔细检查SQL关键字、表名、列名、括号、引号是否拼写正确。使用有语法高亮的编辑器或GUI工具能极大减少这类错误。8.2 违反约束导致的错误当你插入或更新数据时如果违反了表的约束规则如主键重复、外键约束、非空约束、唯一约束操作会失败。错误示例Duplicate entry 1 for key PRIMARY重复的主键值排查确认你要插入/更新的数据是否违反了唯一性主键、唯一索引。确认必填字段NOT NULL是否提供了值。确认外键字段的值是否在关联表中存在。8.3 查询结果不符合预期这通常是由于WHERE条件、JOIN逻辑或对NULL值的理解有误造成的。场景1条件漏选或多选。检查WHERE中的逻辑运算符AND/OR优先级必要时使用括号()明确逻辑。例如WHERE age 18 OR age 25 AND name LIKE 张%和WHERE (age 18 OR age 25) AND name LIKE 张%结果完全不同。场景2对NULL值的处理。记住NULL与任何值包括NULL本身进行比较的结果都是UNKNOWN假。必须使用IS NULL或IS NOT NULL来判断。-- 错误查不到email为NULL的记录 SELECT * FROM students WHERE email NULL; -- 正确 SELECT * FROM students WHERE email IS NULL;场景3多表关联JOIN错误。这属于进阶内容但原理是核心。确保关联条件ON子句正确并理解INNER JOIN、LEFT JOIN的区别。INNER JOIN只返回两个表都匹配的行LEFT JOIN会返回左表的所有行即使右表没有匹配。8.4 性能问题初探为什么我的查询慢当表中数据量变大后一些查询可能会变慢。全表扫描如果WHERE条件中的列没有索引数据库为了找到符合条件的行需要逐行检查整个表这被称为“全表扫描”效率极低。初步优化建议为查询条件列建立索引在经常用于WHERE、JOIN、ORDER BY的列上创建索引可以极大加快查找速度。例如CREATE INDEX idx_students_age ON students(age);避免使用SELECT *只取出需要的列减少数据传输量。谨慎使用LIKE %前缀%以通配符%开头的LIKE查询无法有效利用索引。使用EXPLAIN分析在SQL语句前加上EXPLAIN关键字如EXPLAIN SELECT * FROM students WHERE age 20;数据库会输出查询执行计划告诉你它将如何执行这条语句这是分析慢查询的利器。8.5 编码与字符集问题在插入或查询包含中文等非英文字符时可能会出现乱码。现象数据显示为???或奇怪的字符。解决方案确保数据库、表和连接Connection的字符集Character Set统一设置为utf8mb4推荐支持完整的Unicode包括表情符号。可以在创建数据库和表时指定也可以在连接字符串中指定。掌握“增删改查”只是数据库之旅的起点但却是最坚实的一步。我见过很多开发者因为早期对这些基础操作理解不深导致后期写出的SQL效率低下、漏洞百出。我的建议是不要满足于“能跑通”要多问几个“为什么”为什么这里要加索引为什么用LEFT JOIN而不是INNER JOIN这个WHERE条件会不会导致全表扫描带着这些问题去实践和探索你会更快地成长为一名能真正驾驭数据的开发者。最后再强调一次安全习惯对于UPDATE和DELETE先SELECT确认对于生产环境的数据操作尽量在事务内进行。这些经验都是在踩过坑之后才刻骨铭心的。
返回列表