SQL从入门到上手:建表、查询、索引与实战避坑指南
发布时间:2026/10/2 14:45:34来源:尧图网络
1. 数据库与SQL先把地基打牢1.1 SQL到底是什么为什么非学不可很多刚入行的朋友一听到数据库SQL这两个词就有点发怵总觉得这是高深莫测的东西。实际上你天天都在用数据库——刷朋友圈看到的内容存在数据库里网购下单的订单存在数据库里甚至点外卖时商家看到的接单信息也是从数据库里读出来的。数据库就是存放和管理数据的超级仓库而SQL就是操作这个仓库的通用语言。SQL的全称是Structured Query Language结构化查询语言。它之所以能成为这个领域的普通话是因为几乎所有主流数据库——MySQL、SQL Server、Oracle、PostgreSQL、达梦、SQLite——都支持SQL语法。这意味着你在MySQL上学会的SELECT语句换到Oracle上照样能写只是某些细节略有差异。这种通用性带来的好处是巨大的你只需要学一套语言就能应付绝大多数工作中的数据操作场景。对于零基础的学习者来说SQL可能是所有编程语言里最容易上手的一门。它的语法接近英文的自然表达比如你想查询一张表里的所有数据写SELECT * FROM 表名即可翻译过来就是从表里选出所有内容。这种直觉性的设计让SQL的学习曲线非常平缓。但也正因为简单很多人忽视了系统训练导致工作中写出的SQL要么性能堪忧要么逻辑漏洞百出。这篇文章的主旨就是用一篇的体量把SQL最核心的操作拆开揉碎讲清楚让你能真正上手干活。1.2 五大类SQL语句先建立整体框架学习SQL最容易犯的错误就是一上来就死磕SELECT的各种花哨写法结果连建表都建不明白。我建议你先把SQL的整体框架装进脑子里后面所有的学习都是在往这个框架里填肉。SQL语句按照功能可以划分为五大类我做了张表帮你快速建立认知分类全称作用代表关键字DDLData Definition Language定义和管理数据结构CREATE、ALTER、DROPDMLData Manipulation Language操作表中数据INSERT、UPDATE、DELETEDQLData Query Language查询数据日常用得最多SELECTDCLData Control Language控制权限和访问GRANT、REVOKETCLTransaction Control Language事务管理COMMIT、ROLLBACK举一个生活中的类比DDL相当于你装修房子时砸墙、砌墙、刷漆这些改变房屋结构的动作DML相当于往房间里搬家具、挪家具、扔家具DQL则相当于走进房间看看现在都有什么东西。权限和事务属于更进阶的内容这篇我们重点把DDL、DML和DQL三大块啃透这三块覆盖了日常开发中95%以上的SQL操作。注意不同数据库对DDL和DML的自动提交机制略有差异。MySQL默认每条DML语句自动提交而Oracle需要手动COMMIT。这是初学者换数据库工作时经常踩的坑后面我们会细说。2. 建库建表DDL核心操作详解2.1 数据库与表的创建从零到一很多教程一上来就让你CREATE TABLE但实际工作中你大概率需要先创建一个数据库再在库里建表。以最常用的MySQL为例整个过程分三步走。第一步连接到数据库服务mysql -u root -p输入密码后你就进入了MySQL的命令行环境。第二步创建数据库并指定字符集CREATE DATABASE school DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;这里有个非常关键的经验字符集务必指定为utf8mb4而不是utf8。utf8在MySQL里实际上是utf8mb3只能存储3字节的字符遇到emoji或者某些生僻字就会报错。utf8mb4是完整的4字节编码兼容所有Unicode字符这是我在生产环境踩过坑之后强烈建议你从第一天就养成的习惯。第三步选中数据库并建表。假设我们要建一张学生表USE school; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 主键ID, name VARCHAR(50) NOT NULL COMMENT 姓名, age INT COMMENT 年龄, gender CHAR(1) DEFAULT 男 COMMENT 性别, enroll_date DATE COMMENT 入学日期, score DECIMAL(5,2) COMMENT 平均成绩, created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表;逐个字段拆解id设为主键并自增这是每张表的身份证name用VARCHAR(50)并加NOT NULL约束姓名不能为空age用INT存整数gender用CHAR(1)存定长单字符这里体现了CHAR和VARCHAR的核心区别——CHAR定长、VARCHAR变长短字段用CHAR性能更好score用DECIMAL(5,2)表示最多5位数字、保留2位小数存金额和分数这类需要精确计算的数值时千万不能用FLOAT否则会出现0.10.2不等于0.3的精度问题。ENGINEInnoDB是事务安全的存储引擎支持行级锁和外键约束是MySQL的默认推荐引擎。如果你用MyISAM则完全不具备事务能力这在生产环境中是致命的。初学者建表时最容易犯的错误是想当然地加字段不加注释等过几个月自己都看不懂这张表在干嘛。我的建议是每个字段都加上COMMENT注释这是成本最低收益最高的好习惯。2.2 修改表结构与删除操作慎之又慎建完表之后业务一变表结构大概率要调整。ALTER TABLE语句就是你修改表结构的手术刀。添加字段ALTER TABLE student ADD COLUMN phone VARCHAR(11) COMMENT 手机号;修改字段类型和默认值ALTER TABLE student MODIFY COLUMN gender CHAR(1) DEFAULT 女 COMMENT 性别;修改字段名ALTER TABLE student CHANGE COLUMN score avg_score DECIMAL(5,2) COMMENT 平均成绩;注意MODIFY和CHANGE的区别MODIFY只能改类型和属性不能改字段名CHANGE可以同时改字段名、类型和属性但旧字段名必须写对。删除字段ALTER TABLE student DROP COLUMN phone;删除表和删除数据库DROP TABLE student; DROP DATABASE school;这里必须强调一个血的教训生产环境中执行DROP之前一定要先备份。很多事故都是手一抖表没了。即使是开发环境的DROP也需要再三确认表名正确。我见过有人本想删测试临时表结果因为库没切换对把生产环境的表删了的真实案例。建议你在执行高危操作前先执行SELECT * FROM 表名 LIMIT 1确认一下当前操作的库和表名是否正确再看看有没有开事务DROP操作在MySQL里不支持回滚删了就是真的没了。3. 增删改查DML操作的黄金组合3.1 INSERT插入数据三种写法的适用场景INSERT是给表里塞数据的语句看起来简单但写法不同适用场景天差地别。最基础的单行插入INSERT INTO student (name, age, gender, enroll_date, score) VALUES (张三, 20, 男, 2023-09-01, 88.5);这里有个值得注意的点id字段没出现在列名列表里因为它自增数据库会自动生成created_at也没写因为它有默认值CURRENT_TIMESTAMP。这种列出明确字段名的写法是我强烈推荐的它比INSERT INTO student VALUES (1, 张三, 20, ...)这种省略列名的写法更安全——即使表结构的字段顺序变了你的SQL也不会插入错误的数据。多行批量插入INSERT INTO student (name, age, gender, enroll_date, score) VALUES (李四, 21, 男, 2023-09-01, 78.0), (王五, 19, 女, 2023-09-01, 92.5), (赵六, 20, 女, 2023-09-01, 85.0);批量插入的性能远高于逐条INSERT。假设你要插入1万条数据逐条INSERT需要执行1万次网络往返而批量插入一次搞定。这也是我在数据初始化或数据迁移时最常用的方式。还有一种查询结果直接插入的高级用法用于复制表数据INSERT INTO student_bak (name, age, gender, score) SELECT name, age, gender, score FROM student WHERE score 80;这条语句把成绩大于80的学生记录复制到备份表里整个过程不需要经过应用层效率极高。日常工作里做数据备份、生成报表临时表时经常会用到。3.2 UPDATE修改数据WHERE条件是你的安全绳UPDATE语句的语法很简单UPDATE student SET age 21 WHERE id 1;但就是这个简单的语句几乎每天都在生产环境制造事故。原因只有一个忘记写WHERE条件。UPDATE student SET age 21;这条语句会把全表所有学生的年龄都改成21如果这不是你的本意后果不堪设想。在实际使用中我整理了三条经验第一UPDATE在MySQL里是自动提交的一旦执行无法简单回滚。但在执行前可以先在同一个事务里跑一下BEGIN; UPDATE student SET score 90 WHERE id 100; -- 确认影响行数正确 COMMIT;如果发现不对就执行ROLLBACK能救回来。虽然常规写法不带BEGIN但生产数据操作前开启事务是一个好习惯。第二UPDATE语句里可以配合LIMIT限制影响行数MySQL确实支持UPDATE ... LIMIT 1但这个行为在不同数据库里不一致Oracle等数据库不支持所以强烈不建议依赖LIMIT来保护自己。正确做法是写完UPDATE后先SELECT一下同样的WHERE条件确认你要影响的行数和内容再执行。第三UPDATE支持多表关联更新但初学者往往在这里卡壳。要修改关联表的数据MySQL的写法是UPDATE teacher t JOIN class c ON t.class_id c.id SET t.salary t.salary * 1.1 WHERE c.grade 三年级;意思很清晰把三年级老师工资涨10%。注意MySQL的UPDATE JOIN语法别写错顺序。3.3 DELETE删除数据物理删除与逻辑删除之争DELETE的语法同样简单DELETE FROM student WHERE id 1;不带WHERE的DELETE会清空全表这个跟在UPDATE犯的错误一样致命。但DELETE比UPDATE多了一层物理 vs 逻辑的哲学问题。物理删除就是DELETE语句真正把数据从存储介质里移除。逻辑删除则是给表加一个is_deleted字段删除操作变成UPDATE student SET is_deleted 1 WHERE id 1查询时统一过滤WHERE is_deleted 0。两种做法各有适用场景。对于用户订单、交易流水、合同记录这类业务数据强烈建议用逻辑删除。原因有三一是数据是资产物理删了就没法审计追溯二是误删可以轻松恢复三是业务上往往需要保留历史信息。但是逻辑删除也有代价每个查询都要记得加is_deleted 0条件忘了就会出现幽灵数据表数据量会持续增长查询性能会逐渐下降。作为折中的方案可以定期把逻辑删除超过一定时限的数据做物理清理。如果确实需要清空整张表注意DELETE FROM student和TRUNCATE TABLE student的区别DELETE会逐行删除可以加WHERE条件删除后自增ID不会重置TRUNCATE是直接重建表速度快得多自增ID会重置但TRUNCATE无法回滚且不能加WHERE条件。需要全表清空且确认数据不要了用TRUNCATE更合适。4. 查询是灵魂SELECT核心操作拆解4.1 查询基础WHERE与过滤条件的正确姿势SELECT是SQL里最强大也是最复杂的部分。先从基础说起一个标准的查询长这样SELECT name, age, score FROM student WHERE score 80 AND gender 男;WHERE的过滤条件支持多种运算符等于、不等或!、范围BETWEEN AND、集合IN、模糊匹配LIKE、空值判断IS NULL。有一个高频踩坑点判断NULL不能用或必须用IS NULL或IS NOT NULL。因为NULL在SQL里代表未知任何与NULL的比较结果都是NULL即未知。所以WHERE age NULL永远查不出任何数据。模糊匹配LIKE也有讲究。WHERE name LIKE 张%匹配以张开头的姓名%表示任意长度的任意字符WHERE name LIKE _三匹配某三两个字的姓名_代表单个字符。需要注意LIKE以%开头比如LIKE %三的时候数据库无法使用索引查询会变慢。如果业务上确实需要这种后缀模糊匹配应该考虑改用全文索引或者搜索引擎。还有一个容易被忽略的细节是字符串与日期的比较。在MySQL里写WHERE enroll_date 2023-09-01没问题但如果表里存的是DATETIME类型且包含时间部分这个等值比较会查不到数据因为实际存储值是2023-09-01 10:30:00。正确写法是用日期函数截断WHERE DATE(enroll_date) 2023-09-01。4.2 多表连接JOIN从笛卡尔积到精确关联单表查询只是热身实际业务中最常遇到的是多表关联查询。比如要查每个学生的班级名称和班主任就需要把student表和class表连起来。JOIN的核心思想是把两张表按某种关联条件拼在一起。先看最常用的内连接SELECT s.name, c.class_name, t.name AS teacher_name FROM student s INNER JOIN class c ON s.class_id c.id INNER JOIN teacher t ON c.teacher_id t.id;这里我给表起了别名student用s表示class用c表示。表别名不仅让SQL更简洁也是多表查询时的规范做法否则两张表有同名字段时SELECT后面必须用表名.字段名来区分。误解最多的就是JOIN类型。我用一张表解释清楚JOIN类型含义返回结果INNER JOIN内连接只返回两表都匹配上的记录LEFT JOIN左连接返回左表全部记录右表无匹配则补NULLRIGHT JOIN右连接返回右表全部记录左表无匹配则补NULLFULL OUTER JOIN全连接返回两表所有记录MySQL不直接支持用一个例子说明LEFT JOIN的行为假设左表是学生表右表是班级表LEFT JOIN会把没有班级class_id为空或班级不存在的学生也查出来此时右表的班级字段为NULL。这在用户管理和订单查询里非常有用查所有用户及其订单即使没下过单也显示出来。初学JOIN时最容易出错的写法是忘记ON条件或者关联条件写错导致产生笛卡尔积——两表记录两两组合数据量呈爆炸式增长。比如学生表1000条记录、班级表50条记录忘记ON条件就会查出50000条。所以写完JOIN后第一时间检查结果数是否符合预期。4.3 分组聚合GROUP BY与HAVING统计报表的基石GROUP BY是SQL中思维转变的一个分水岭从查明细变成做统计。它的作用是按某列或多列把数据分组然后对每组做聚合计算。MySQL支持的聚合函数包括COUNT计数、SUM求和、AVG平均值、MAX最大值、MIN最小值。例如统计每个班级的平均分、人数和最高分SELECT class_id, COUNT(*) AS student_count, AVG(score) AS avg_score, MAX(score) AS max_score FROM student GROUP BY class_id;这里COUNT(*)统计每组的行数AVG(score)计算每组的平均分。注意一个铁律SELECT后面出现的非聚合列必须出现在GROUP BY里。上面这段SQL如果写成SELECT name, COUNT(*) FROM student GROUP BY class_idMySQL的ONLY_FULL_GROUP_BY模式会直接报错而且这本身就是逻辑错误——每个分组里有多个name你到底想显示哪个如果要给分组加过滤条件不能使用WHERE而要使用HAVING。两者的区别是WHERE在分组前过滤原始行HAVING在分组后过滤聚合结果。比如找出平均分大于85的班级SELECT class_id, AVG(score) AS avg_score FROM student GROUP BY class_id HAVING AVG(score) 85;新手常犯的错误是把别名用在HAVING里HAVING avg_score 85在MySQL里碰巧能用但在Oracle和SQL Server里就会报错因为HAVING子句的执行顺序在SELECT别名生效之前。为了跨数据库兼容建议HAVING里使用表达式不要用别名。GROUP BY对多个列的分组在实际业务中同样高频比如统计每个年级每个班级的男女生人数SELECT grade, class_id, gender, COUNT(*) AS cnt FROM student GROUP BY grade, class_id, gender;这会按三列的组合进行分组每个组合生成一行汇总结果。4.4 排序与分页让结果可读可用排序的关键字是ORDER BY默认升序ASC可以指定降序DESC还可以多列排序SELECT name, score, age FROM student WHERE score 60 ORDER BY score DESC, age ASC;这段SQL先按分数从高到低排列分数相同时按年龄从小到大排列。ORDER BY的执行优先级在WHERE之后所以你可以在排序前先用WHERE缩小数据范围。多列排序时列的先后顺序决定了主要和次要条件这个细节在生成排行榜时特别重要。分页是Web系统里最常见的需求。MySQL用LIMIT实现SELECT * FROM student ORDER BY id LIMIT 10 OFFSET 20;LIMIT 10表示返回10条记录OFFSET 20表示跳过前20条。这对应的是第3页数据每页10条。分页的通用公式是LIMIT 每页条数 OFFSET (页码 - 1) * 每页条数。不同数据库的分页语法差异很大我做了个对照表数据库分页写法MySQL / PostgreSQLLIMIT 10 OFFSET 20SQL ServerOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLYOracleFETCH FIRST 10 ROWS ONLY12c或ROWNUM嵌套子查询分页查询还有一个性能隐患OFFSET越大数据库需要扫描并丢弃的行越多越到后面的页越慢。比如LIMIT 10 OFFSET 1000000MySQL会先扫出1000010行再丢掉前1000000行。优化方案是改用键集分页记录上一页最后一条数据的ID用WHERE id 上次最大ID ORDER BY id LIMIT 10来取下一页效率可以提升几个数量级。5. 进阶利器子查询、视图与索引5.1 子查询的灵活运用子查询就是嵌套在查询里的查询它返回的结果可以作为外层查询的过滤条件或数据源。子查询解决了一步查不出来分两步走的问题。最经典的场景是查询比平均分高的学生SELECT name, score FROM student WHERE score (SELECT AVG(score) FROM student);这个括号里的子查询先算出全表平均分再作为外层WHERE的比较值。子查询执行一次结果固定这种叫不相关子查询。还有一种相关子查询子查询会引用外层查询的别名。比如查询每个班级中成绩最高的学生信息SELECT s.name, s.score, s.class_id FROM student s WHERE s.score ( SELECT MAX(score) FROM student WHERE class_id s.class_id );外层每处理一行子查询都重新执行一次查询班级里最高的分数。这种写法逻辑清晰但性能开销较大表数据量大的时候要慎用。子查询的另一种用法是作为临时表放在FROM后面。比如先求出每个班的最高分再把这个结果跟student表关联SELECT t.name, t.score, c.class_name FROM ( SELECT class_id, MAX(score) AS max_score FROM student GROUP BY class_id ) m JOIN student t ON t.class_id m.class_id AND t.score m.max_score JOIN class c ON c.id m.class_id;这种先聚合、后连接的模式在统计报表中非常常见。注意子查询作为临时表必须有别名上面例子里的m就是别名去掉会直接报错。5.2 视图把复杂查询包装成虚拟表视图是SQL里一个性价比极高的功能。它本质是一个保存好的SELECT查询使用时可以像表一样去SELECT但视图本身不存储数据所有数据仍然来自基础表。创建视图CREATE VIEW v_student_info AS SELECT s.id, s.name, c.class_name, t.name AS teacher_name FROM student s LEFT JOIN class c ON s.class_id c.id LEFT JOIN teacher t ON c.teacher_id t.id;创建之后你每次查询SELECT * FROM v_student_info WHERE class_name 三班;相当于把上面那串复杂的JOIN语句封装了起来应用层只需要面对一个简单的视图。这对团队协作特别友好后端写好的复杂统计逻辑数据分析同学只需要对着视图做简单查询即可。视图还有一个实用价值是权限控制。比如只允许业务人员看到学生的姓名、班级和成绩但不让他们看到手机号、家庭住址等敏感字段就可以创建只包含必要字段的视图授予查询权限。需要注意两点第一视图在MySQL中通常是不可更新的即使能更新也限制极多不要试图通过视图去修改基础表数据第二如果底层表结构发生变化视图可能失效需要重新创建或使用CREATE OR REPLACE VIEW更新。5.3 索引查询提速的关键武器数据量小的时候SQL随便怎么写都很快但当表数据量涨到百万级没有索引的查询会慢到让你怀疑人生。索引的本质是一种有序的数据结构让数据库不必逐行扫描全表就能快速定位数据。创建索引CREATE INDEX idx_student_name ON student(name);创建联合索引CREATE INDEX idx_class_score ON student(class_id, score);联合索引是索引优化里最讲究的部分。上面的联合索引(class_id, score)表示先按class_id排序class_id相同再按score排序。查询时如果WHERE条件同时包含class_id和score这个索引就非常高效如果只含score而不含class_id索引大概率用不上。这里要理解最左前缀原则联合索引的生效遵循最左边的字段优先原则跳过了第一个字段直接用后面的字段索引会失效。哪些字段适合建索引我的经验是频繁出现在WHERE条件里的字段、JOIN的关联字段、ORDER BY的排序字段都值得建索引。而频繁更新的字段、重复值占比极高的字段比如性别字段只有男女两个值则不适合建索引建立后反而拖慢写入速度。索引的内容很深但对初学者来说记住两大原则就够用了第一索引不是越多越好每张表的索引数量控制在5个以内因为每次写入数据都要同时更新所有索引第二对SELECT频繁的表建立合理的索引收益远大于代价。注意使用SELECT * FROM student WHERE name LIKE %三%这种以通配符开头的模糊查询即使有索引也无法使用。这是索引失效最常见的场景之一。6. 实战踩坑SQL常见问题与排查技巧6.1 常见报错与解决方案SQL报错是每个初学者的必经之路不要慌九成以上的错误原因就那么几类。我把高频报错整理成速查表报错信息常见原因解决方式Unknown column xxx字段名写错用DESC 表名查看真实字段名SQL syntax error语法错误检查关键字拼写、逗号、引号是否成对Column xxx in field list is ambiguous多表查询时同名字段未加表别名使用表名.字段名明确指定You cant specify target table for update in FROM clause子查询引用了正在更新的表改用临时表或JOIN方式Incorrect string value字符集不支持特殊字符表和连接都使用utf8mb4Lock wait timeout exceeded行锁等待超时检查是否有长事务未提交Data too long for column字段长度不够使用ALTER TABLE增大长度举两个真实案例拆解。有个朋友在生产环境执行UPDATE student SET score score 5 WHERE id IN (SELECT id FROM student WHERE score 60)MySQL直接报错cant specify target table for update in FROM clause。原因是MySQL不允许在子查询里直接引用正在UPDATE的同一张表。改成临时表方案UPDATE student SET score score 5 WHERE id IN (SELECT id FROM (SELECT id FROM student WHERE score 60) AS tmp);外面再套一层子查询把数据搬运一下问题就解决了。再比如Lock wait timeout exceeded这个报错出现在事务长时间占用某行记录的锁而另一个事务等待超时。解决思路是找到并结束持有锁的事务。MySQL里可以用SELECT * FROM information_schema.innodb_trx;查出所有运行中的事务找到trx_state为RUNNING且持续很久的线程用KILL 线程ID终止。6.2 慢SQL优化心得在实际开发中SQL能用只是第一步生产环境对性能的要求往往更苛刻。一条慢SQL能把整个数据库拖垮这就涉及慢SQL优化。先说排查手段。MySQL默认开启了慢查询日志你可以定位最耗时的SQLSHOW VARIABLES LIKE slow_query_log; SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 2;超过2秒的查询会被记录到日志文件里。拿到慢SQL后用EXPLAIN查看执行计划EXPLAIN SELECT * FROM student WHERE score 80;EXPLAIN的结果里有一列叫type从好到差依次是system、const、eq_ref、ref、range、index、ALL。看到ALL表示全表扫描这是性能杀手range表示索引范围扫描通常可以接受const和ref表示精确命中了索引是最理想的状态。慢SQL优化的常规手段按优先级排序优化查询逻辑减少不必要的字段返回避免SELECT *把数据量大但逻辑复杂的查询拆成多个简单查询在应用层合并加索引或调整索引针对慢SQL的WHERE和JOIN条件建立合适索引避免在索引列上做函数运算WHERE YEAR(enroll_date) 2023会让索引失效应改写为WHERE enroll_date 2023-01-01 AND enroll_date 2024-01-01优化大表分页用键集分页代替深分页OFFSET考虑数据归档把历史数据迁到归档表核心表只保留热数据查询大幅提速一个典型的优化案例某个报表查询需要扫描500万行数据执行耗时8秒。排查后发现WHERE条件里对日期列使用了LEFT(enroll_date, 7) 2023-09这种写法让日期索引完全失效。改写为enroll_date 2023-09-01 AND enroll_date 2023-10-01之后执行时间从8秒降到0.05秒提升了160倍。这个案例告诉我们很多时候性能问题不是数据库不行而是SQL写法有问题。6.3 新手必须要养成的几个好习惯文章最后分享几条我在实际工作里逐渐沉淀下来的习惯。这些习惯不一定写在教科书上但正是它们让我少踩了很多坑。第一写SQL之前先搞清楚表结构。用DESC查看表结构搞清楚每个字段的含义、类型、是否有索引。连表结构都没搞清楚就急着写查询等于蒙着眼睛开车。我见过太多人因为没先看字段类型拿字符串跟数字比或者不知道时间是DATETIME还是TIMESTAMP导致查询结果为空。第二生产环境操作必须备份和开事务。不管是UPDATE还是DELETE数据库默认自动提交执行错了很难恢复。养成先查再改、改前备份的习惯关键时刻能救命。即使你的公司有完整的备份机制从备份恢复到止损也需要时间这段时间里业务可能已经受到影响。第三写查询时先写SELECT再逐步加过滤条件。不要一步到位写一长串SQL那样出错后极难排查。先把基础查询跑通确认返回的数据结构正确然后一层一层加WHERE、JOIN、GROUP BY、ORDER BY。每次加完条件都跑一遍确认结果符合预期再继续。这个方法在调试复杂查询时能帮你节省大量时间。第四注意SQL注入风险。在Web应用里永远不要用字符串拼接的方式把用户输入直接嵌进SQL语句。用户输入 OR 11 --这种内容时拼接出来的SQL可能会把整个表的数据查出来或者造成更严重的破坏。应用层应该使用参数化查询或预编译语句这是防注入最有效的手段。第五选一个趁手的数据库图形化工具。命令行能用但日常开发效率低。我常用的Navicat、DBeaver、DataGrip这些工具都支持可视化建表、执行查询、查看执行计划。特别是DBeaver开源免费跨平台支持几乎所有主流数据库装一个能用好几年。工欲善其事必先利其器别在这上面省钱省时间。最后再说一点个人体会SQL这门技能看十篇教程不如上手练十道题。建议你找一台机器装个MySQL或者直接用在线SQL练习平台把文章里的示例SQL亲手跑一遍再自己改改条件、加加字段看看会得到什么结果。踩过的坑、跑通的心得都会变成你自己的经验。等你能不看文档随手写出带JOIN、GROUP BY的查询时恭喜你SQL已经从入门到上手了。
网站建设高端定制企业官网