1. RDBMS(关系型数据库)
RDBMS:建立在关系模型的基础上,由多张相互连接的二维表组成的数据库。
使用表储存数据,格式统一,便于维护;用SQL语言操作,标准统一,使用方便
注释:--单行注释 # 单行注释(mysql特有) /多行注释/
2. sql的分类
DDL(data definition language) 数据定义语言,用来定义数据库对象
DML(data manipulation language)数据操作语言,用来对数据库表中的数据进行增删改
DQL(data query language)数据查询语言 ,用来查询数据库中的表的记录
DCL(data controllanguage)数据控制语言,用来创建数据库用户,控制数据库访问的权限
2.1.DDL 数据定义语言
数据库操作:
查询 show databases;
创建 create database if not exists bookstore;
删除 drop database() if exists 数据库名;
使用 use 数据库名;
表操作:
添加字段 alter 表名 add 字段名 类型(长度)
修改数据类型: alter table 表名 modify 字段名 新数据类型(长度);
修改字段名和数据类型: alter table 表名 change 旧字段名 新字段名 类型;
修改表名 alter table 表名 rename to 新表名;
删除字段 alter table 表 drop 字段名; truncate(删内容) table 表名;
这些ALTER TABLE语法在实际开发中很少手敲,通常用可视化工具搞定,或者交由 DBA 执行。但作为 SQL 基础,无疑是重要的,尤其是DROP(连表带结构都删掉)、TRUNCATE(清数据,表结构还在)、DELETE(按行删数据,可加where条件,支持回滚,表结构还在)的区别一定要分清。
#建表语句,可参考 CREATE TABLE IF NOT EXISTS user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', username VARCHAR(50) NOT NULL COMMENT '用户名' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';2.2.DML 数据库操作语言
增加数据:INSERT INTO 表名 (列名1, 列名2) VALUES (值1, 值2), (值3, 值4);
修改:UPDATE 表名 SET 字段名1=值1, 字段名2=值2 WHERE 条件;
删除:delete from 表名;(表数据全部删掉)
避坑指南:
1.对于修改数据和删除数据来说where条件就是生命线。如update user set name = '张三’;若无where,则全部语句都将变成张三;同理,dalete from user;运行后,全表的数据立刻清空
2.在实际的开发中,很少用delete把数据从硬盘删除(因为无法修复)。通常用逻辑删除is_deleted字段(比如 0 代表正常,1 代表已删除),用UPDATE去修改这个字段的状态。
3.DML 和 DDL 最大的区别是DML(增删改)操作支持事务回滚。如果误删数据,只要没有commit,就可以通过rollback(回滚)撤销操作,从而恢复数据
4.对于主键冲突(id已存在),此时用insert会报错。通常会采用以下操作
INSERT IGNORE INTO ... 忽略错误
INSERT INTO ... ON DUPLICATE KEY UPDATE ... 有则更新,无则插入
REPLACE INTO .. 替换 其中第二条最常用
2.3.DQL数据查询语言
基本查询 去重复记录 select distinct 字段列表 from 表名
条件查询 where name like ‘_ _’ (%模糊匹配的区别。%代表任意个字符,_代表单个字符)
select *from ... where... group by ... having ... order by ... limit...
聚合查询 count,max,min,avg,sum SELECT COUNT(*), AVG(salary) FROM 表名;
分组查询 group by (用having过滤)
排序查询 order by(asc升序 desc降序)
分页查询 select 字段 from 表名 limit 起始索引,查询记录数;(mysql方言)
起始索引 =(当前页码 - 1) * 每页条数
书写顺序:SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT ...
2.4.DCL数据控制语言
-- 1.1. 创建用户 (注意:'%'代表任意主机,'localhost'代表本机) --CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码'; CREATE USER 'zhangsan'@'%' IDENTIFIED BY '123456'; -- 1.2. 修改用户密码 ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码'; -- 1.3. 删除用户 DROP USER '用户名'@'主机名'; -- 2.1. 授予权限 (GRANT) -- 语法:GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名'; -- 示例:给 zhangsan 授予 bookstore 库所有表的 查询 和 插入 权限 GRANT SELECT, INSERT ON bookstore.* TO 'zhangsan'@'%'; -- 示例:授予所有权限 (ALL) GRANT ALL ON *.* TO 'zhangsan'@'%'; -- 允许 zhangsan 把自己的权限再授权给别人(相当于封了个小管理员) GRANT ALL ON *.* TO 'zhangsan'@'%' WITH GRANT OPTION; -- 2. 撤销权限 (REVOKE) -- 语法:REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名'; -- 示例:撤销 zhangsan 对 bookstore 库的插入权限 REVOKE INSERT ON bookstore.* FROM 'zhangsan'@'%'; -- 撤销所有权限 REVOKE ALL ON *.* FROM 'zhangsan'@'%';3. 常用函数
3.1.字符串函数
CONCAT(s1, s2, ...) | 字符串拼接(有null结果就是null) |
SUBSTRING(str, start, length) | 截取字符串(如手机后四位) |
UPPER(str)/LOWER(str) | 转大写 / 转小写 |
TRIM(str) | 去除字符串首尾空格(常用来清洗用户输入的数据) |
如:把姓和名拼起来
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM employees;
3.2.数值函数
ROUND(x, y) | 四舍五入。x是数值,y是保留的小数位数 |
CEIL(x) | 向上取整(天花板)。(比如分页计算总页数:CEIL(总条数 / 每页条数)) |
FLOOR(x) | 向下取整(地板) |
MOD(x, y) | 取余数(也就是x % y,常用来判断奇偶数) |
ABS(x) | 绝对值 |
3.3.日期函数
NOW() | 返回当前系统的日期和时间(YYYY-MM-DD HH:MM:SS) |
CURDATE() | 返回当前日期(YYYY-MM-DD) |
DATEDIFF(date1, date2) | 计算两个日期相差的天数。(例如:计算用户注册了多少天) |
DATE_FORMAT(date, format) | 格式化日期,如把日期转成2023-10-01或2023年10月。 |
如:查询最近 7 天注册的用户
SELECT * FROM users WHERE create_time >= DATE_SUB(NOW(), INTERVAL 7 DAY);
3.4.流程函数
IF(value, t, f) | 如果 value 为真,返回 t,否则返回 f |
IFNULL(value1, value2) | 如果 value1 不为空,返回 value1;否则返回 value2。 (常用于处理空值,比如计算奖金时 |
| 最强流程控制,类似于 Java 里的switch-case或if-else。 |
-- 将数据库里的状态码转成中文 SELECT name,CASE WHEN status = 1 THEN '启用' WHEN status = 0 THEN '禁用' ELSE '未知' END AS status_desc FROM users;4.约束
| NOT NULL | 非空约束 | DEFAULT | 默认约束 |
| UNIQUE | 唯一约束(允许null) | CHECK | 检查约束(保证满足某一条件) |
| PRIMARY KEY | 主键约束(不允许null) | FOREIG KEY | 外键约束 |
CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, -- 主键约束(自带非空+唯一)+ 自增 name VARCHAR(20) NOT NULL, -- 非空约束 gender CHAR(1) DEFAULT '男', -- 默认约束 phone VARCHAR(11) UNIQUE, -- 唯一约束 age INT CHECK (age > 0 AND age < 150), -- 检查约束 class_id INT, FOREIGN KEY (class_id) REFERENCES class(id) -- 外键约束 );笔者提示:刚开始学的时候,一定会先学外键保证数据一致性,但实际开发,大多禁止外键,原因如下:
a.性能:每次插入、更新数据时,数据库都要去检查外键约束,在高并发下极其影响性能。
b.死锁:外键容易导致表与表之间产生级联锁定,引发死锁。
c.分库分表:在大数据量下,数据库要拆分,外键根本无法跨库生效。
5. 多表查询
在实际业务中,数据往往分散在多个表中,多表查询无疑是重要的。在讲连接之前,先理清数据库设计的三大关系:
一对一:用户表与用户详情表。
一对多:部门与员工(最常见,通常在多的一方添加外键)。
多对多:学生与课程(必须建立中间表,存放两张表的主键)。
5.1.笛卡尔积(交叉连接)
当两张表连接时,未指定连接条件,就会产生笛卡尔积(结果条数 = A表条数 × B表条数)
SELECT * FROM 表A, 表B;
5.2 多表查询
a.内连接 inner join 查询两表交集部分
隐式内连接:使用WHERE过滤SELECT*FROM 表A a,表B b WHEREa.id = b.a_id;
显式内连接:使用INNER JOIN.ONSELECT*FROM 表A a INNER JOIN 表B b ON a.id = b.a_id;
b.外连接(OUTER JOIN)查询某一张表的全部数据,以及两张表交集的数据。|
左外连接 (LEFT JOIN):查询左表所有数据,以两表交集部分。若右表无对应数据显示NULL。
右外连接 (RIGHT JOIN):查询右表所有数据,以及两表交集部分。
SELECT * FROM 表A a LEFT JOIN 表B b ON a.id = b.a_id;
c. 自连接 (Self Join)当前表与自身进行连接查询。常用于处理同一张表内的层级关系(如:员工表与主管表)注意自连接必须给表起别名,否则数据库无法区分是哪张表。
·查询员工及其直属主管的名字(员工和主管都在 emp 表里)
SELECT e.name AS '员工', m.name AS '主管'
FROM emp e
LEFT JOIN emp m ON e.manager_id = m.id;
d.联合查询 (UNION)把多次查询的结果合并起来,形成一个新的查询结果集。要求多次查询的列数必须相同,数据类型必须兼容。 union自动去重 union all 不去重,将结果拼接
示例:查询薪资 < 5000 或者 年龄 > 50 的员工
SELECT * FROM emp WHERE salary < 5000 UNION ALL
SELECT * FROM emp WHERE age > 50;
5.3子查询 (嵌套查询)
a.标量子查询结果为单个值,常配合=,<>,>,<使用。
SELECT * FROM emp WHERE salary > (SELECT AVG(salary) FROM emp);
b.列子查询结果为一列,常配合IN,NOT IN使用。
select * from emp where dept_id >all(select salary from emp where dept_id = (select id from dept where name = “财务部”))
c.行子查询结果为一行,通常用于同时匹配多个字段。
如:查询薪资和主管ID都与某个特定员工相同的其他员工
SELECT * FROM emp WHERE (salary, managerid) = (SELECT salary, managerid FROM emp WHERE name = '张三');
d.表子查询多行多列。常放在FROM后面作为派生表,或者配合IN使用。
SELECT * FROM emp WHERE (job, salary) IN (SELECT job, salary FROM emp WHERE dept_id IN (1, 2));
6. 事务
简介:把所有操作作为一个整体一起向系统提交或撤销操作,同时成功或同时失效
6.1事务操作
select @@autocommit; set @@autocommit = 0; --设置为手动提交 --1.查询张三账户余额 select * from account where name = 'zhangsan'; --2.将张三账户余额-1000 update account set money -1000 where name = 'zhangsan'; --3.将李四账户余额加1000 update account set money +1000 where name = 'lisi'; submit; --提交事务 --回滚事务 rollback; ========================================================= select @@autocommit; set @@autocommit = 1; --设置为自动提交 start transction; --1.查询张三账户余额 select * from account where name = 'zhangsan'; --2.将张三账户余额-1000 update account set money -1000 where name = 'zhangsan'; --3.将李四账户余额加1000 update account set money +1000 where name = 'lisi'; commit; --提交事务 --回滚事务 rollback6.2 事务的四大特性 (ACID)
原子性 (Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败
一致性 (Consistency):事务完成时,必须使所有的数据保持状态一致(比如转账前后,A 和 B 的总金额不变)。
隔离性 (Isolation):数据库系统提供的隔离机制,让事务在不受外部并发操作影响的独立环境下运行。
持久性 (Durability):事务一旦提交或回滚,它对数据库中数据的改变就是永久的(哪怕数据库宕机,重启后数据依然存在,因为已经写入了磁盘)。
6.3 并发事务引发的问题
当多个事务并发(同时)执行时,如果没有做好隔离,会引发以下问题
脏读(Dirty Read):一个事务读到了另一个事务还没有提交的数据。(如果那个事务回滚了,读到的就是脏数据)。
不可重复读(Non-repeatable Read):一个事务先后读取同一条记录,但两次读取的数据不同。(因为中间另一个事务修改并提交了这条数据)。
幻读(Phantom Read):一个事务按照条件查询数据时,没有对应的数据行;但在插入数据时,发现这行数据已经存在(或者再次查询时,凭空多出了一些数据,像产生了幻觉)。(注:不可重复读针对单行数据的 UPDATE,幻读针对多行范围的 INSERT/DELETE)。
6.4 事务隔离级别
为了解决并发事务带来的脏读、不可重复读和幻读问题,数据库提供了四种隔离级别。隔离级别越高,数据越安全,但并发性能越低。
由低到高依次为:
读未提交(Read uncommitted):最低级别。一个事务可以读到另一个事务未提交的数据。无法解决任何并发问题(脏读、不可重复读、幻读都可能发生)。
读已提交(Read committed):只能读到已经提交的数据。能解决“脏读”,但无法解决“不可重复读”和“幻读”。(这是 Oracle 数据库的默认级别)。
可重复读(Repeatable Read):保证在一个事务内,多次读取同一条记录的结果是一致的。能解决“脏读”和“不可重复读”。(这是 MySQL 数据库的默认级别)。
串行化(Serializable):最高级别。强制事务串行执行(加锁排队),完全隔离。能解决所有并发问题,但并发性能极差,通常只用于对数据一致性要求极高的极端场景。
文章到这里就结束了。以上就是关于 SQL 数据库核心知识体系的全部梳理。从基础的增删改查,到复杂的连接查询,再到保证数据安全的事务控制,掌握这些,你已经具备了扎实的 SQL 功底。建议大家多在自己电脑上建几张表,把文中的 SQL 语句亲手跑一遍,遇到报错才是真正成长的开始。祝大家在面试和工作中都能游刃有余!我们一起加油啊!!