下面的是SQL要注意的问题,有些是我踩过的坑,有些是同事踩的。大家看的时候可能没啥感觉,等到不小心搞出问题后会想起来的哈哈哈
谨慎考虑使用
这方法是分组的形式将相同的行进行字符串聚合,字符之间默认以逗号间隔,有点耗时的,用了时候考虑一下要聚合的行数,如果行数多的话慎用。有同事之前SQL超时就因为这个
至于它的用处,举个例子:
要把全校的同学以班级为单位分班
select class as 班级 , GROUP_CONCAT(studentName) as 学生名单 from student group by class
班级 | 学生名单 |
---|---|
1班 | 小明,小白,小黄 |
2班 | 小猪,二狗子 |
我不知道怎么称呼,我也没见过有人这么写过,长这样子:
select studentName as 学生姓名, (select grade from student_grade where class = '1班') as 成绩 from student_info where class = '1班'
假设1班有50个人
根据SQL的执行顺序:先from -> where ->select,我们每查一个同学的姓名,就去查一次全班的成绩(即select grade就执行1次);
一句话,外查询的SQL返回多少行(50个studentName 共50行),内查询就查几次,共多查了50次,如果外查询返回了50万行呢
建议内查询少用
join需要用到临时表内存,会生成一张临时表,对于几百万数据量的大表,用 left join 就得谨慎点 了:
错误示范:
select .. from 600万行的表 left join 1000行的表 on ...
对于 left join,不管on后面的条件是什么,都会生成一个600万行的临时表,你的数据库的临时表内存八成会爆掉的哦
因为left join导致左边的大表几乎是被全表扫描了,那么索引大概率也不会生效了。
left join + where 是说在生成临时表后,用where的条件对临时表进行过滤删减
错误示范:
# (1) DATE_FORMAT会导致日期索引失效,假设create_time是索引
select record_info as 日志内容 from record_log where DATE_FORMAT(create_time,'%Y-%m-%d')='2021-02-23'
# (2) age是int 型, 且作为索引列, 参与了算术运算, 导致索引失效
'age' int(11) Not NULL DEFAULT 0 COMMENT '年龄'
select studentName as 学生姓名 from student_info where age+1 = 18
第一条SQL的DATE_FORMAT会导致日期索引失效,因为数据库会对create_time进行逐行计算后再比对,那基本是全表扫描
第二条SQL对索引列age进行运算,会导致索引失效
表里的字段是什么类型,传入的参数就给什么类型的
错误示范:
# age是字符型, 且为索引
'age' varchar(6) Not NULL DEFAULT '0' COMMENT '年龄'
alter table student_info add index index_age('age')
select studentName as 学生姓名 from student_info where age = 18
age作为索引列
如果项目的数据库有分库分表的话,查询的时候尽量带上分库键,它能将SQL语句分发到指定的库表去执行,我们这的DRDS是这样子的
如果不带分库键(拆分键)的话,会导致SQL语句在全库都进行扫描执行,很慢的
创建复合索引
alter table student_info add index index_collection('age','studentName','sex')
只有以下三种where语句的顺序会走复合索引:
另外,第一索引列age涉及 “ >,<,between and ”,就停用索引了。
因为涉及范围查询 时,B+树的索引查找是 “ 直接从左到右的方式遍历叶子节点组成的链表 ”,而不是从根节点从上往下查找了
group by和order by后面带索引能降低开销
特别是索引列的定义为Default NULL的话会很影响稳定性
列的定义最好有个默认值,即NOT NULL DEFAULT VALUE
特别是表明 状态类型 的字段,要给出所有状态的数值和对应的含义,例如:
`state` int(11) NOT NULL DEFAULT 0 COMMENT'0 进行中, 1 完成, 2 失效'
text是长文本的数据类型,mysql服务器将text数据传回客户端需要消耗大量的网络带宽,而服务器查询的时候将text数据从磁盘加载到内存又需要大量的IO带宽。
因此,在定义字段的数据类型时,text一般只适用于无法确定数据的长度的时候使用,其他情况慎用。
什么是区分度高?就是索引列的字段值最好都不一样,重复的越少,区分度越高。主键就是区分度最好的,每一行都是不重复的值,B+树很容易就将所有的行划分开来。
那种 区分度低的列最好别上索引,比如性别sex,无非就是男或女,会有很多数据行重复,想象一下:
如果要查出 成绩高于90分的男生是谁,一共50个人,有49个男生…
在成绩表grade 将性别sex设置为索引
alter table grade add index index_sex('sex')
select studentName from grade where sex='男' and grade>90
预想的执行情况是把49个男生拿出来,再一个个对比成绩,这跟没上索引差不多…
因为 索引sex=‘男’ 出现的频率过高,估计MySQL的执行策略会变成全表扫描也说不定呢
然而我司就有这种问题,咱也不敢说什么。。。
B+树的结点要存储索引列的每一个值,如果索引列的每个值都很大的话,MySQL会在把索引加载进入内存的过程中消耗大量的IO带宽
另外,索引字段的数据长度小的话,占用的内存就小,你知道的,innodb_buffer_pool_size设置的缓存索引的空间是有限的。 索引字段的数据长度越小,内存就能够容纳的更多的键值,提高1次就能查找到目标值的几率。1次找不到,就得从磁盘再次加载其他索引去查
能用int类型,就尽量不要用BigInt
主键更应该让数据长度小一点,每个二级索引(除聚簇索引外的索引)都会存放索引字段(除了主键外的其他字段)+对应的主键,主键太长,那每个二级索引占用的内存越大
其他的就不知道了,写不动了。。。