MySql知识
join 组合两个表
SELECT * FROM A JOIN B a ON A.id = B.user_id;
- inner join 内连接,返回两个表中都存在的行
- left join 左连接,返回 A 表中所有的行,即使 B 表中没有对应行
- right join 右连接,返回 B 表中所有的行,即使 A 表中没有对应行
- full join 全连接,返回 A 和 B 表中都存在的行,以及 A 表中没有,B 表中有的行,以及 B 表中没有,A 表中有的行
limit 限制返回的行数
SELECT * FROM user LIMIT M, 10;跳过 M 行后返回十条数据SELECT * FROM user LIMIT 10 OFFSET M
having 过滤分组
1 | |
聚合函数
聚合函数的作用就是就是对有多个名字相同的结果聚合
聚合函数要搭配 group by (字段名) 使用
比如这个按商品名聚合,然后把同名商品的销售额全加在一起作为总的销售额
简单来说就是,根据列名分组,把列里面属性相同的值分成组然后压成一条数据,此时保留或计算的这个属性是根据聚合函数指定的,可以是求和 sum,保留最小值 min,保留最大值 max 统计行数 count 求平均值等等
1 | |
假设有个学生表,保留每个学科的最高分
1 | |
聚合函数
- count() 统计行数
- sum() 求和
- avg() 平均值
- max() 最大值
- min() 最小值
配套语法
- group by() 分组
- having() 过滤分组
- distinct() 去重
执行顺序
先 from 再 where 最后 select
1 | |
变量声明
语句中只能用常量,不能用表达式
1 | |
模糊查询
CONCAT()是拼接字符串的函数,本意为连接SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')
#{}和${}
- 绝大部分都用#{}
- #{}是预编译参数,可以防止 SQL 注入,MyBatis 会把 #{name} 转换成 JDBC 的 ? 占位符
- ${}直接字符串替换,MyBatis 直接把 ${name} 替换成你传入的值,原样插入 SQL
MySQL 的索引
MySQL 的索引都是 B+树的结构,叶子结点每个都是一个页,然后页内的每一条记录就是真实存储的数据(按主键组织的 B+数存真实数据,其他索引需要回表,回到主键索引来查真实数据)
每个页的页号和页的最小记录主键构建 B+树的非叶子结点,也就是索引(主键索引、聚簇索引)
根据索引查找就是从根节点渐渐二分找,到达叶子结点存储的页,从页内再找对应的记录
对应 MySQL 来说,主键索引一定存在,不设置主键 MySQL 会优先选一个 Unique 键作为主键索引,这个也没有就自己生成一个默认主键
DB_ROW_ID
普通索引(二级索引)
- 实际上是另一个 B+树,不过这个每个叶子结点存的不是数据而是索引列的值(下面的 name 就是) + 主键值
- 假设给 name 建立一个索引,这次不通过主键排序后二分,而是通过 name 排序(字典序)后二分查找,最后到叶子结点的时候拿到对应记录的主键来找真实数据,这样通过 name 找数据就变得非常快
联合索引
也是 B+树结构,假设为 name 列和 phone 列建立索引,叶子结点存储的就是 name 列,phone 列,和主键值,此时的排序就是先按 name 列排序,name 列相同再按 phone 列排序,直接搜索 phone 是乱序的,最后找到主键值后回表查真实数据
就假设联合索引是(a,b,c),在 a 查完之后,此时所有的相同的 a 的索引对应的 b 才是有序的,才能利用到联合索引去查 b,b 查完才能查 c,不查 a 直接查 b 是无效的,因为 b 整体无序,局部有序(这个局部就是相同的 a 的条件下,这个就是最左匹配原则)
如果只查询 a 的条件时,完全可以直接使用这个联合索引,不需要再单独为 a 建立一个索引,相当与 a 的二级索引(普通索引)
只有当 a 是等于时,b 才能利用到联合索引,只有当 a 和 b 都是等于时,c 才能利用到联合索引
减少或避免回表的操作
索引覆盖(覆盖索引)
- 索引中已经包含了所有查询的数据,不需要回表的查询方式叫做索引覆盖
- 假设是为 name 和 phone 建立联合索引,又碰巧查的数据就只有 name 和 phone,此时到叶子结点时就根本不需要回表
索引下推(索引条件下推、ICP)
正常索引的使用是在存储引擎,而条件的过滤是在 Server 层
索引下推就是把条件过滤下推到存储引擎,让其通过索引来完成
假设 name 和 phone 建立联合索引,查询语句是
select * from user where name = '张三' and phone = '%1566%'假设查到的叫张三的有一万个人,而手机号实际符合的只有 1 个人,拿就有 9999 条无关数据而且他们都是回表查出来的索引下推后直接让存储引擎先从这一万条数据中选出合法的那一条,然后再进行回表,就只需要回表一次
索引失效
- 违反最左匹配原则
- 聚合函数不能用索引
- 表达式不能用索引
where id + 1 = 1100,但是修改一下where id = 1100 - 1就可以用索引了 - 索引类型为字符串时,用数字搜索会失效;但反过来,索引为数字,用字符串来搜索能用到。因为 MySQL 自动把字符串转换成数字
- or 前是索引列但之后不是索引列,就不能用索引
但是具体用不用索引还是取决于 MySQL 的优化器,根据开销(cost)来判断,选开销最小的,如果都很大就直接全表扫描了
索引创建原则
- 不为离散度低的列创建索引,比如性别只有男女
- 只为搜索、排序或分组的列创建索引
- 用好联合索引
- 过长的字段创建前缀索引,该列的前几个字符,避免占用过大空间
- 频繁更新的列不作为主键或索引