ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL入门必备:从基础语法到高频查询、索引与事务实战

MySQL入门必备:从基础语法到高频查询、索引与事务实战 如果你最近正在学 MySQL或者刚把 MySQL 装好却不知道从哪里开始这篇笔记就是给你准备的。不管是后端开发、数据分析、运维还是测试岗位MySQL 几乎是你绕不开的第一个数据库工具而所有复杂的 SQL本质上都是从一条SELECT、一个WHERE、一次ORDER BY长出来的。这篇“基础语法与高频查询”第一课我会把建库建表、增删改查、过滤排序、分组统计、索引与事务这些入门必备环节串起来讲尽量用贴近日常工作的例子让你可以照着敲、能复现、少踩坑。1. 准备工作装库、建库、建表先把环境跑起来学习 SQL 最忌讳只看不敲所以第一步一定先把 MySQL 环境跑通。不要追求一步到位先能连上控制台再慢慢补概念。这个过程踩的坑越多后面理解越深。1.1 安装与连接别在第一步卡太久Windows 用户可以直接从 MySQL 官网下载 MySQL Installer安装时选择 Server only 或默认的 Developer Default 都可以。Linux 用户建议优先用发行版自带的包管理器比如 Ubuntu 可以用apt install mysql-serverCentOS 可以用yum install mysql-server。如果你不想把本机环境弄乱或者需要在几台机器之间快速切换版本用 Docker 是最省事的方案。这里给一个 Docker 快速部署 MySQL 8.0 的常用命令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -e MYSQL_DATABASEtestdb \ mysql:8.0等容器起来后用docker exec -it mysql8 mysql -uroot -p就能进到 MySQL 命令行。本地已经装好 MySQL 的直接打开终端执行mysql -uroot -p输入密码成功进入后你会看到mysql提示符说明环境没问题。客户端工具我建议优先用 MySQL Workbench官方免费且升级稳定如果觉得 Workbench 太笨重用 DBeaver 或 Navicat 试用版也可以。这里多说一句不要为了找个破解版客户端浪费时间开源的 DBeaver 功能完全够入门以后真正需要用商业工具的自然会理解付费价值。1.2 建库建表字符集和排序规则别用老默认值很多新手第一步就踩坑建表时不指定字符集导致存中文变成乱码。MySQL 8.0 默认字符集已经是utf8mb4但如果你用的是 5.7 或更早版本最好还是显式指定。utf8mb4才是完整的 Unicode 编码普通utf8在 MySQL 里其实存不下所有中文场景下的特殊字符。先建库CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;建表时把字段类型、主键、索引一次性设计好避免后面反复改。以一张最典型的用户表为例USE shop; CREATE TABLE user ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, email VARCHAR(100) DEFAULT , age TINYINT UNSIGNED DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里面有几个老手一看就懂、新手容易忽略的细节id用BIGINT UNSIGNED是为了应付以后数据量增长TINYINT UNSIGNED用来存年龄因为年龄不会超过 255created_at直接给默认值CURRENT_TIMESTAMP插入时就不用手动写时间。表引擎务必用InnoDB因为它支持事务和行级锁这是 MyISAM 不具备的能力。1.3 表结构维护ALTER 与 DROP 的正确姿势表建错了不要慌用ALTER TABLE改。举几个高频场景-- 新增字段 ALTER TABLE user ADD COLUMN phone VARCHAR(20) DEFAULT ; -- 修改字段类型 ALTER TABLE user MODIFY COLUMN age SMALLINT UNSIGNED DEFAULT 0; -- 删除字段 ALTER TABLE user DROP COLUMN phone; -- 查看表结构 DESC user;删除表用DROP TABLE清空表但保留表结构用TRUNCATE TABLE。这两者的区别很重要TRUNCATE相当于一次性删除所有行无法在事务里回滚而且会重置自增主键DELETE FROM table不加 WHERE 也能清空数据但逐行删除自增值不会重置。入门期建议只用DELETE并准确定位到行避免误操作。2. 基础语法增删改查的原生姿势建好表之后接下来就是每天都要用的四大操作增、删、改、查。查会放在后面单独说但插入、更新、删除的语法同样重要因为这三个操作一旦写错代价往往比查询大得多。2.1 SELECT 查询的基础结构与执行顺序一个完整的查询语句大致长这样SELECT column1, aggregate(column2) FROM table_name WHERE condition GROUP BY column1 HAVING aggregate_condition ORDER BY column1 LIMIT n;很多新手只记住了关键字顺序却不理解执行顺序导致百思不得其解的报错。MySQL 实际的执行顺序是FROM - WHERE - GROUP BY - HAVING - SELECT - ORDER BY - LIMIT理解这个顺序后有两个常见问题就迎刃而解第一WHERE阶段还没生成聚合结果所以WHERE里不能直接用COUNT(*) 5这种条件必须放在HAVING里第二SELECT阶段在ORDER BY之前所以ORDER BY可以引用SELECT里定义的别名WHERE却不行。2.2 插入数据INSERT 语法、批量插入、注意事项插入单条记录是最基础的INSERT INTO user (username, email, age) VALUES (tom, tomexample.com, 23);也可以一次插入多行减少客户端与数据库的交互次数INSERT INTO user (username, email, age) VALUES (jerry, jerryexample.com, 25), (ann, annexample.com, 28);这里要注意字段列表和值列表必须一一对应。如果省略字段列表就必须给所有不允许为 NULL 的字段赋值否则会报Field xxx doesnt have a default value。实际开发中我习惯显式列出字段名一方面可读性好另一方面表结构增加字段时不容易批量报错。批量插入虽然快但也不是越多越好。一次插几万行容易把 binlog 撑大而且长时间持有锁。建议控制在每批 500 到 1000 行左右配合事务提交更稳妥。2.3 UPDATE 和 DELETE加了 WHERE 才算安全更新和删除是高风险操作一个不小心就会造成无法挽回的数据变更。新手最容易犯的错误就是忘记 WHERE或者 WHERE 条件写得太宽。-- 正确只更新指定用户 UPDATE user SET age 24 WHERE username tom; -- 危险所有用户年龄都被改了 UPDATE user SET age 24;删除同理-- 安全操作 DELETE FROM user WHERE id 10; -- 危险操作 DELETE FROM user;我的习惯是在执行UPDATE或DELETE之前先用同样的WHERE条件跑一条SELECT看看到底会影响到哪些行。比如要更新前先执行SELECT id, username, age FROM user WHERE username tom;确认结果没有问题再把它改成UPDATE。这个习惯在清理线上数据时能救命。3. 高频查询过滤、排序、分页与聚合入门阶段最有价值的内容就是这四件事过滤数据、排序、分页、分组统计。日常业务里的查询超过八成都是这类操作。把这块吃透你至少能应付大部分初级开发岗位的 SQL 面试题。3.1 WHERE 过滤你真的会写条件吗WHERE是所有查询的基础重点关注比较运算、模糊匹配和空值判断。比较运算容易理解直接用、!、、、、即可SELECT id, username, age FROM user WHERE age 18 AND age 30;这里有个新手容易忽略的点AND优先级高于OR。如果你要把多个条件混合一定用括号明确分组SELECT * FROM user WHERE age 18 OR (age 60 AND status vip);模糊查询用LIKE%表示任意多个字符_表示单个字符SELECT * FROM user WHERE username LIKE tom%; SELECT * FROM user WHERE email LIKE %example.com;需要特别提醒LIKE %xxx%这种左右都有百分号的写法在数据量大时无法利用索引会全表扫描。如果业务真的频繁需要模糊搜要么考虑全文索引要么接受全表扫描不能指望数据库索引通吃。空值判断用IS NULL或IS NOT NULL千万不要写成 NULL。因为NULL表示“未知值”任何与NULL的直接比较结果都是NULL也就是不会返回任何行。SELECT * FROM user WHERE email IS NULL;IN和BETWEEN也是高频写法SELECT * FROM user WHERE age IN (18, 20, 25); SELECT * FROM user WHERE created_at BETWEEN 2024-01-01 AND 2024-12-31;3.2 ORDER BY 排序和 LIMIT 分页高频中的高频排序语法不复杂关键在于多字段排序的语义。先按一个字段再按另一个字段SELECT id, username, age FROM user ORDER BY age DESC, id ASC;上面这个语句会先按age倒序排年龄一样的再按id正序排。写多列排序时每个字段都要单独指定方向容易漏掉第二列的ASC。另外ORDER BY后面可以写数字表示按SELECT字段的序号排序但在实际项目里可读性太差不建议用。分页是查询里的固定动作。MySQL 有两种等价写法-- 跳过前 20 行取 10 行 SELECT * FROM user ORDER BY id LIMIT 20, 10; -- 等价写法和含义更清晰的写法 SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 20;LIMIT 20, 10表示从第 21 行开始取 10 行第一个数字是偏移量第二个数字是行数这个顺序太容易记反。我自己现在写分页一律用LIMIT 10 OFFSET 20一眼就能看出是“取 10 条偏移 20 条”。这里还有一个隐藏性能坑偏移量越大MySQL 需要扫描并丢弃的行越多。比如LIMIT 100000, 10实际上会先读 100010 行再丢弃前 100000 行。优化办法是记录上一页最后一条记录的 ID用范围条件代替偏移量SELECT * FROM user WHERE id 100000 ORDER BY id LIMIT 10;这种“基于游标”的分页方式在数据量大时性能远胜OFFSET写法。3.3 GROUP BY 聚合与 HAVING 过滤统计报表的基本功统计每天的新增用户、订单数、平均客单价这些需求都离不开聚合函数也就是COUNT、SUM、AVG、MAX、MIN。配合GROUP BY可以做分组统计。以用户表为例按性别分组统计人数和平均年龄SELECT gender, COUNT(*) AS cnt, AVG(age) AS avg_age FROM user GROUP BY gender;COUNT(*)统计的是行数COUNT(email)统计的是非 NULL 值个数两者结果可能不同。如果只想统计某一列非空的记录数直接用COUNT(column)更准确。分组后再过滤要使用HAVINGSELECT gender, COUNT(*) AS cnt FROM user GROUP BY gender HAVING cnt 100;这个HAVING cnt 100在GROUP BY之后执行所以能引用聚合结果。它的执行位置比WHERE晚所以性能上通常低于WHERE。能先用WHERE过滤掉的行绝不放到HAVING里。比如只统计活跃用户分组就应该先在WHERE status active过滤而不是分组后再HAVING。4. 进阶必知索引、事务与复杂查询基础查询练熟以后就该认识一些直接影响查询性能和保障数据安全的概念。这几个点既是面试高频也是实际开发中写 SQL 时刻要想着的问题。4.1 索引让查询从全表扫描变成快速定位索引就像书的目录没有索引时MySQL 只能一页页翻全表有索引后可以迅速定位到目标行。但索引不是越多越好因为每次写入都要同步维护索引会让更新变慢。最常用的创建索引方式ALTER TABLE user ADD INDEX idx_age (age); CREATE INDEX idx_email ON user(email);判断一个查询有没有用上索引用EXPLAIN看执行计划EXPLAIN SELECT * FROM user WHERE username tom;在结果里关注type和key两列。只要key不为空说明用上了索引type从好到差大致是const、ref、range、index、ALL其中ALL就是最差的全表扫描。新手最容易碰到索引失效的几种情况对索引列使用函数WHERE YEAR(created_at) 2024类型不一致导致隐式转换字段是字符串却传了数字模糊查询左没写死WHERE username LIKE %tom使用OR连接非索引列WHERE username tom OR age 18这些场景我都踩过排查时只要看到执行计划是全表扫描优先检查这些写法。4.2 事务与锁数据一致性不可忽视InnoDB引擎支持事务事务要满足ACID四个特征原子性、一致性、隔离性、持久性。入门理解这三个控制关键字就够BEGIN或START TRANSACTION开启事务COMMIT提交ROLLBACK回滚。举一个典型的转账场景START TRANSACTION; UPDATE account SET balance balance - 100 WHERE id 1; UPDATE account SET balance balance 100 WHERE id 2; -- 都成功了才提交 COMMIT; -- 如果中途失败执行 ROLLBACK;没有事务的情况下第一条UPDATE成功、第二条失败钱就凭空消失了。有了事务后两条操作要么全部生效要么全部不生效。事务靠锁来保证并发安全。InnoDB默认使用行级锁但某些条件下会退化成表级锁。最典型的情况是更新时WHERE字段没有索引MySQL 只能锁定全表。高并发场景下这条 SQL 会把整张表锁住导致大量请求堆积。锁的分类在面试里常问简单记一个维度就够了按粒度分表锁和行锁按模式分共享锁读锁和排他锁写锁。入门阶段先记住结论业务里优先使用有索引的字段作为更新条件能走行锁就走行锁。4.3 JOIN 与子查询多表查询别再连错日常业务几乎不可能只用单表用户表、订单表、商品表之间要关联查询。最常用的是INNER JOIN和LEFT JOIN。INNER JOIN只返回两边都能匹配上的行SELECT o.id, u.username, o.amount FROM orders o INNER JOIN user u ON o.user_id u.id WHERE o.status paid;LEFT JOIN返回左表全部行右表没有匹配的就是NULLSELECT u.username, COUNT(o.id) AS order_cnt FROM user u LEFT JOIN orders o ON o.user_id u.id GROUP BY u.id;这里最容易搞混的是ON和WHERE的区别。ON控制两个表怎么关联WHERE控制关联后过滤结果。对于LEFT JOIN如果条件写在WHERE里再限定右表字段可能导致右表空值的行被过滤掉实际效果退化成INNER JOIN。子查询也常用尤其是和聚合函数配合SELECT id, username FROM user WHERE age (SELECT AVG(age) FROM user);子查询会把内层结果缓存或临时表化复杂场景下性能不一定比JOIN好。入门阶段如果看到子查询里嵌套了很多层建议先试试改写为JOIN再用EXPLAIN对比验证。5. 常见问题排查实录学习过程中一定会遇到各种报错很多问题网上搜半天最后发现原因特别蠢。我把新手阶段最容易踩的问题整理一下按环境连接、SQL 执行和常见报错三个方向说透。5.1 连接与启动问题最大的痛点是服务起不来和连不上。Windows 上执行net start mysql提示服务无法启动大概率是my.ini路径不对或3306端口被占用。先在命令行执行netstat -ano | findstr :3306看端口被哪个进程占用找到 PID 后去任务管理器结束进程或者修改 MySQL 配置里的端口号。提示 SSL 连接错误多数是客户端和服务器 SSL 版本不匹配。本地测试环境最简单的处理方式是连接参数里加useSSLfalse生产环境则应该正确配置证书而不是一关了之。提示Access denied for user rootlocalhost说明密码错误或 root 账号只允许本机登录。确认密码后用mysql -uroot -p重试即可。如果记得 root 密码只是想给某个 IP 开权限可以单独建用户不要直接改 root 的登录范围。5.2 SQL 执行异常中文乱码是最常见的问题。你明明在命令行建表时写了中文查询出来却是问号或者乱码。原因通常是连接字符集和表字符集不一致。登录后执行SET NAMES utf8mb4;命令行本身是gbk时也可以试SET NAMES gbk。不过根治办法还是建库建表时统一用utf8mb4并在连接字符串里显式指定characterEncodingutf8。排序结果“感觉不对”比如按中文名字排序顺序完全不符合预期。这是因为存储字符集和排序规则不同。使用utf8mb4_0900_ai_ci时中文排序一般是按 Unicode 编码顺序如果希望按拼音排需要在排序时指定SELECT username FROM user ORDER BY CONVERT(username USING gbk);注意这种写法会放弃索引数据量小时用用可以数据量大要另想方案。执行更新语句时报You are using safe update mode这是 MySQL Workbench 的误操作保护机制不允许不带主键条件的UPDATE或DELETE。解决办法不是关闭安全模式而是把条件写清楚如果只是测试可以在Edit - Preferences - SQL Editor里临时关闭。5.3 新手高频错误速查表这里整理几张问题、原因、解决办法的对照表方便以后遇到直接查。错误提示常见原因解决办法ERROR 1064SQL 语法错误关键字拼错或逗号漏掉检查语句结尾的分号、关键字大小写、字符串引号ERROR 1045Access denied用户名或密码不对确认用户名密码确认账号允许的来源 IPERROR 1049Unknown database数据库不存在确认USE database_name;里名字没写错ERROR 1054Unknown column列名不存在用DESC 表名;查看实际字段名ERROR 1146表不存在确认表名大小写、所在库是否正确ERROR 1366Incorrect string value中文数据插入失败确认表字符集是utf8mb4连接字符集一致ERROR 1451外键约束阻止删除/修改先处理关联子表数据或临时关闭外键检查ERROR 1175Safe update mode 不允许无主键条件更新补充精确WHERE条件不要盲目关安全模式ERROR 2003Cant connect to MySQL server确认服务启动、端口开放、防火墙放行ERROR 2013Lost connection during query网络不稳或单次查询数据量过大这些报错里1064、1366、1175 是我见过新手问得最多的三个。每次看到报错不要急着复制到搜索引擎先看错误编号和尾部的信息提示大部分问题其实已经很直白。最后再分享一个我个人的习惯学 SQL 时我会自己建三张“伪业务表”用户表、订单表、商品表每张表几十行数据专门用来练JOIN、GROUP BY、子查询和分页。数据量不大但关系清楚跑任何查询都能一眼看出结果对不对。这篇文章里的例子基本都是这套表结构下写出来的。你没必要背语法把今天这些高频查询原原本本敲一遍比收藏十篇入门教程都管用。
返回列表