SQL语句优化

MySQL和Oracle语法

MySQL大表优化

索引(Index)相关及索引原理(B树,B+树)

覆盖索引

一、优化细则

  1. 在 where 子句中使用 != 或 <> 操作符,会导致引擎放弃使用索引而进行全表扫描。SQL中,不等于操作符会限制索引,引起全表扫描,即使比较的字段上有索引。MySQL只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些方式的LIKE('a%')。

模糊查询效率很低原因:like本身效率就比较低,应该尽量避免查询条件使用like。对于like ‘%...%’(全模糊)这样的条件,是无法使用索引的,全表扫描自然效率很低;另外,由于匹配算法的关系,模糊查询的字段长度越大,模糊查询效率越低。
  解决办法:首先尽量避免模糊查询,如果因为业务需要一定要使用模糊查询,则至少保证不要使用全模糊查询,对于【右模糊查询like ‘…%’是会使用索引的;左模糊like ‘%...’无法直接使用索引】,但可以利用reverse + function index 的形式,变化成 like ‘…%’。全模糊是无法优化的,一定要的话考虑用搜索引擎。出于降低数据库服务器负载的考虑,尽可能地减少数据库模糊查询。

  1. 注意范围查询语句
    对于联合索引来说,如果存在范围查询,比如between、>、<等条件时,会造成后面的索引字段失效。

  2. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。如果排序字段没有用到索引,就尽量少排序。根据实际情况进行调整,因为有时索引太多也会降低性能。

  • order by的列尽量被索引。order by的列如果被索引,性能也会更好
  • 不使用order by rand() limit ~MySQL的随机抽取实现方法
select id from table order by rand() limit 1000;

上面的SQL语句,可优化为:

select id from table  t1 
join  (select rand() * (select max(id) from table ) as nid) t2 on t1.id > t2.nid
limit 1000;
  1. 在 where 子句中对字段进行 null 值判断,会导致引擎放弃使用索引而进行全表扫描,如:

理由:并不是说使用了 is null 或者 is not null 就会不走索引了,这个跟 MySQL 版本以及查询成本都有关。如果 MySQL 优化器发现,走索引比不走索引成本还要高,肯定会放弃索引,这些条件 !=,>,is null,is not null 经常被认为让索引失效,其实是因为一般情况下,查询的成本高,优化器自动放弃索引的。如果把 null 值,换成默认值,很多时候让走索引成为可能,同时,表达意思会相对清晰一点。【见第21点】

  1. 在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
SQL语句优化_第1张图片
  1. 在 where 子句中对字段进行函数、算术运算或其他表达式运算,会导致引擎放弃使用索引而进行全表扫描。如:
算术运算
SQL语句优化_第2张图片
函数操作
  1. 为列选择合适的数据类型,而且要避免隐式类型转换
  • where子句中出现column字段的类型和传入的参数类型不一致的时候会发生类型转换,建议先确定where中的参数类型。
  • 能用TINYINT就不用SMALLINT,能用SMALLINE就不用INT,磁盘和内存消耗越小越好。

【如果字段类型是字符串,where 时一定用引号括起来,否则索引失效】
反例:

select * from user where userid = 123; 

正例:

select * from user where userid = '123'; 

为什么第一条语句未加单引号就不走索引了?这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL 会做隐式的类型转换,把它们转换为浮点数再做比较。

  1. 结果集允许重复的话,尽量用union all代替union
    union 和 union all 的差异,主要是前者不管检索结果有没有重复,都会尝试进行合并,然后在输出最终结果前进行排序、过滤操作,增加大量的CPU运算,加大资源消耗及延迟。而UNINON ALL不去重,效率高于UNION。当然,union all 的前提条件是两个结果集没有重复数据。或者如果结果集允许重复的话,尽量使用union all 代替 union 。

  2. 如果限制条件中其他字段没有索引,尽量少用 or。or两边的字段中,如果有一个不是索引字段,而其他条件也不是索引字段,会造成该查询不走索引的情况。应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描。很多时候使用 union all 或者是 union(必要的时候)的方式来代替“or” 会得到更好的效果。新建一个 user 表,它有一个普通索引 userId,表结构如下:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` int(11) NOT NULL,
  `age` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_userId` (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

假设现在需要查询 userId 为 1 或者年龄为 18 岁的用户,很容易有以下 SQL。
反例:

select * from user where userid=1 or age =18

正例:

//使用union all 
select * from user where userid=1 
union all 
select * from user where age = 18

//或者分开两条sql写:
select * from user where userid=1
select * from user where age = 18
理由:使用 or 可能会使索引失效,从而全表扫描。

对于 or+没有索引的 age 这种情况,假设它走了 userId 的索引,但是走到 age 查询条件时,它还得全表扫描,也就是需要三步过程:全表扫描+索引扫描+合并,如果它一开始就走全表扫描,直接一遍扫描就完事。MySQL 是有优化器的,处于效率与成本考虑,遇到 or 条件,索引可能失效,看起来也合情合理。

  1. 将大的 delete,update 或者 insert 查询变成多个小查询

    为了更优的性能以及更好的数据控制,应该将大的 sql 写成多个小查询。避免同时修改或删除过多数据,因为会造成 CPU 利用率过高,从而影响别人对数据库的访问。
    SQL语句优化_第3张图片

反例:

//一次删除10万或者100万+?
delete from user where id <100000;
//或者采用单一循环操作,效率低,时间漫长
for(User user:list){
   delete from user; 
}

正例:

//分批进行删除,如每次500
delete user where id<500
delete product where id>=500 and id<1000;

理由:一次性删除太多数据,可能会有 lock wait timeout exceed 的错误,所以建议分批操作。

【关于 delete 优化】一个 delete 语句执行了2个多小时了,还没执行完,能不能把这个 SQL 回滚?答案是不能,如果直接回滚,结果会更惨。一个 SQL 能不能回滚,或者说进行到了什么程度?不建议暴力重启,先执行一个“show engine innodb status\G”语句,这样能看到执行时间。如果没有运行状态,就可以 Kill 掉。

  1. 如果插入数据过多,考虑批量插入

反例:

for(User u :list){
 insert into user(name,age) values(#name#,#age#)   
}

正例:

//一次500批量插入,分批进行
insert into user(name,age) values

    (#{item.name},#{item.age})

理由:批量插入性能好,更加省时间。
打个比喻:假如需要搬一万块砖到楼顶。有一个电梯,电梯一次可以放适量的砖(最多放 500),可以选择一次运送一块砖,也可以一次运送 500 块砖,哪个时间消耗大?

  1. select语句务必指明字段名称,尽量避免使用"select * "。因为它会进行全表扫描,不能有效利用索引,增加很多不必要的消耗(CPU、IO、内存、网络带宽),增大了数据库服务器的负担,以及它与应用程序客户端之间的网络IO开销。当表结构发生改变时,前端也需要更新。所以要求直接在select后面接上字段名。

  2. Inner join 、left join、right join,优先使用 Inner join。如果是 left join,左边表结果尽量小。Inner join 内连接,在两张表进行连接查询时,只保留两张表中完全匹配的结果集。left join 在两张表进行连接查询时,会返回左表所有的行,即使在右表中没有匹配的记录。right join 在两张表进行连接查询时,会返回右表所有的行,即使在左表中没有匹配的记录。都满足 SQL 需求的前提下,推荐优先使用 Inner join(内连接),如果要使用 left join,左边表数据结果尽量小,如果有条件的尽量放到左边处理。

  • MySQL中没有full join,可以用以下方式来解决:
select * from A 
left join B on B.name = A.name where B.name is null
union all 
select * from B;
  • 尽量使用inner join,避免left join:
    参与联合查询的表至少为2张表,一般都存在大小之分。如果连接方式是inner join,在没有其他过滤条件的情况下MySQL会自动选择小表作为驱动表;但是left join在驱动表的选择上遵循的是左边驱动右边的原则,即left join左边的表名为驱动表。
  • 合理利用索引:
    被驱动表的索引字段作为on的限制字段。
  • 利用小表去驱动大表
    从原理图能够直观的看出如果能够减少驱动表的话,减少嵌套循环中的循环次数,以减少 IO总量及CPU运算的次数。
  • 巧用STRAIGHT_JOIN:
    inner join是由MySQL选择驱动表,但是有些特殊情况需要选择另个表作为驱动表,比如有group by、order by等「Using filesort」、「Using temporary」时。STRAIGHT_JOIN来强制连接顺序,在STRAIGHT_JOIN左边的表名就是驱动表,右边则是被驱动表。在使用STRAIGHT_JOIN有个前提条件是该查询是内连接,也就是inner join。其他链接不推荐使用STRAIGHT_JOIN,否则可能造成查询结果不准确。
    这个方式有时能减少3倍的时间。

反例:

select * from tab a left join tab1 b on a.size = b.size where a.id>2; 

正例:

select * from (select * from tab where id >2) a left join tab1 b on a.size = b.size; 

理由如下:
如果 inner join 是等值连接,或许返回的行数比较少,所以性能相对会好一点。
同理,使用了左连接,左边表数据结果尽量小,条件尽量放到左边处理,意味着返回的行数可能比较少。

【不要有超过 5 个以上的表连接】连表越多,编译的时间和开销也就越大。把连接表拆开成较小的几个执行,可读性更高。如果一定需要连接很多表才能得到数据,那么意味着设计非常糟糕。

  1. SQL语句中 in 和 not in 也要慎用,否则会导致全表扫描。MySQL对于in做了相应的优化,即将in中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。对于连续的数值,能用between就不要用in,再或者使用连接来替换。
  1. 区分 in 和 exists、not in 和not exists

区分 in 和 exists 主要是造成了驱动顺序的改变(这是性能变化的关键)。如果是exists,那么以外层表为驱动表,先被访问;如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;exists适合于外表小而内表大的情况。

关于 not in 和 not exists,推荐使用not exists,不仅仅是效率问题,not in 可能存在逻辑问题。如何高效的写出一个替代 not exists 的SQL语句?

原SQL语句:

select colname … from tableA 
where a.id not in (select b.id from tableB)

高效的SQL语句:

select colname … from tableA 
left join tableB on a.id = b.id 
where b.id is null

取出的结果集为A表不在B表中的数据。

【exist&in 的合理利用】

假设表 A 表示某企业的员工表,表B表示部门表,查询所有部门的所有员工,很容易有以下 SQL:

select * from A where deptId in (select deptId from B);

这样写等价于:

先查询部门表B
select deptId from B
再由部门deptId,查询A的员工
select * from A where A.deptId = B.deptId

可以抽象成这样的一个循环:

List<> resultSet ;
    for(int i=0;i

显然,除了使用 in,也可以用 exists 实现一样的查询功能,如下:

select * from A where exists (select 1 from B where A.deptId = B.deptId);

因为 exists 查询的理解就是,先执行主查询,获得数据后,再放到子查询中做条件验证,根据验证结果(true 或者 false),来决定主查询的数据结果是否得意保留。那么,这样写就等价于:

select * from A 先从A表做循环
select * from B where A.deptId = B.deptId 再从B表做循环。

同理,可以抽象成这样一个循环:

List<> resultSet ;
    for(int i=0;i

数据库最费劲的就是跟程序链接释放。假设链接了两次,每次做上百万次的数据集查询,查完就走,这样就只做了两。相反建立了上百万次链接,申请链接释放反复重复,这样系统就受不了了。

即 MySQL 优化原则,就是小表驱动大表,小的数据集驱动大的数据集,从而让性能更优。因此,我们要选择最外层循环小的,也就是,如果 B 的数据量小于 A,适合使用 in,如果 B 的数据量大于 A,即适合选择 exist。

  1. 【推荐】in操作能避免则避免,若实在避免不了,需要仔细评估in后边的集合元素数量,控制在1000个之内。

  2. 对于复合索引来说,要遵守最左前缀法则。在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件才能保证系统使用该索引,否则该索引将不会被使用。如果条件都用上了,但是顺序不同,现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。但是应尽可能的让字段顺序与索引顺序相一致。

创建一个联合索引,如(id,name,school),相当于创建了(id)、(id,name)和(id,name,school)三个索引。直接用 id,或者 id、name 这样的顺序可以命中索引;id、school 只有 id 部分用到了索引;name、school 无法使用这个索引。所以在创建复合索引的时候一定要注意索引字段顺序:①常用的查询字段放在最前面。②需要考虑字段值去重之后的个数,较多的放前面。

联合索引不满足最左原则,索引一般会失效,但是这个还跟 MySQL 优化器有关的。

  1. 必要时可以使用force index来强制查询走某个索引
    有的时候MySQL优化器采取它认为合适的索引来检索SQL语句,但是可能它所采用的索引并不是我们想要的。这时就可以采用force index来强制优化器使用我们制定的索引。

  2. 【强制】不要使用count(列名)或count(常量)来替代count(*),count(*)是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。

说明:count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行。

  1. 不要写一些没有意义的查询,如需要生成一个空表结构:
    select col1,col2 into #t from t where 1=0
    这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
    create table #t(...)

  2. 并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引。如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

  3. 索引并不是越多越好

  • 索引并不是越多越好,索引虽然提高了查询的效率,但是也降低了插入和更新的效率。
  • insert 或 update 时有可能会重建索引,所以建索引需要慎重考虑,视具体情况来定。
  • 一个表的索引数最好不要超过 6 个,若太多需要考虑一些索引是否没有存在的必要。
  1. 应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

  2. 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,否则会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

反例:

king_id` varchar(20) NOT NULL COMMENT '守护者Id'

正例:

`king_id` int(11) NOT NULL COMMENT '守护者Id'`
  1. 尽可能的使用 varchar/nvarchar 代替 char/nchar 。因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

反例:

`deptName` char(100) DEFAULT NULL COMMENT '部门名称' 

正例:

`deptName` varchar(100) DEFAULT NULL COMMENT '部门名称' 
  1. 尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

  2. 避免频繁创建和删除临时表,以减少系统表资源的消耗。

  3. 临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。

  4. 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

  5. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

  6. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

  7. 使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

  8. 与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

  9. 在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

  10. 尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

  11. 尽量避免大事务操作,提高系统并发能力。

  12. 使用EXPLAIN分析 SQL 计划。EXPLAIN可以检查索引使用情况以及扫描的行。日常开发写 SQL 的时候,尽量养成用 explain 分析 SQL 的习惯,尤其是走不走索引这一块。

  13. 慎用 distinct 关键字

distinct 关键字一般用来过滤重复记录,以返回不重复的记录。在查询一个字段或者很少字段的情况下使用时,给查询带来优化效果。但是在字段很多的时候使用,却会大大降低查询效率。

反例:

select distinct * from user; 

正例:

select distinct name from user; 

理由:带 distinct 的语句 CPU 时间和占用时间都高于不带 distinct 的语句。因为当查询很多字段时,如果使用 distinct,数据库引擎就会对数据进行比较,过滤掉重复数据,然而这个比较、过滤的过程会占用系统资源,CPU 时间。

【强制】count(distinct col) 计算该列除NULL之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。

  1. 【强制】当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,因此使用sum()时需注意NPE问题。

正例:可以使用如下方式来避免sum的NPE问题:SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;

  1. 【强制】使用ISNULL()来判断是否为NULL值。

说明:NULL与任何值的直接比较都为NULL。

1) NULL<>NULL的返回结果是NULL,而不是false。

2) NULL=NULL的返回结果是NULL,而不是true。

3) NULL<>1的返回结果是NULL,而不是true。

  1. 【强制】不得使用外键与级联,一切外键概念必须在应用层解决。

说明:以学生和成绩的关系为例,学生表中的student_id是主键,那么成绩表中的student_id则为外键。如果更新学生表中的student_id,同时触发成绩表中的student_id更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。

  1. 【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。

  2. 【强制】数据订正(特别是删除、修改记录操作)时,要先select,避免出现误删除,确认无误才能执行更新语句。

  3. 【参考】如果有国际化需要,所有的字符存储与表示,均以utf-8编码,注意字符统计函数的区别。

说明:
SELECT LENGTH("轻松工作"); 返回为12
SELECT CHARACTER_LENGTH("轻松工作"); 返回为4

如果需要存储表情,那么选择utf8mb4来进行存储,注意它与utf-8编码的区别。

  1. 【参考】 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但TRUNCATE无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此语句。 说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。

  2. 为获得相同结果集的多次执行,请保持SQL语句前后一致。这样做的目的是为了充分利用查询缓冲。比如根据地域和产品ID查询产品价格,第一次使用了:
    select price from order where id='123' and region='BEIJING'
    那么第二次同样的查询,请保持以上语句的一致性,比如不要将where语句里面的id和region位置调换顺序。

  3. 为了提高 group by 语句的效率,可以在执行到该语句前,把不需要的记录过滤掉。

反例:

select job,avg(salary) from employee
group by job 
having job ='president' or job = 'managent'

正例:

select job,avg(salary) from employee
where job ='president' or job = 'managent' 
group by job
  1. 索引不会包含有NULL值的列
    只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以在数据库设计时不能让字段的默认值为NULL。

  2. 索引列排序
    MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引

  3. 字符字段只建前缀索引

  4. 字符字段最好不要做主键

  5. 尽量不用UNIQUE,由程序保证约束

  6. 明知只有一条查询结果,那请使用 “LIMIT 1”

“LIMIT 1”可以避免全表扫描,找到对应结果就不会再继续扫描了。

  1. varchar应给设置一个合适的长度而不是给一个很大的长度,因为MySQL建立索引时一般会默认使用的该字段的长度,占用存储空间且降低性能,另外,所有列总长度被限制为65,535字节。

  2. text、blob字段应该从频繁查询的表中分离出去,因为text、blob之类的长列会完全被MySQL保存在一个单独的数据页里面里,查询效率低。

  3. 给整型字段设置长度并无意义,int(1)与int(11)通常情况下没有任何区别。

  4. 为每张表都设置一个id做为其主键,最好也设置AUTO_INCREMENT标志。

  5. 不要轻易使用查询缓存,特别是写密集型应用,缓存失效会占用大量的系统资源。

二、Oracle中的in参数的个数限制:

Oracle 中 in 后括号中的参数个数有限制,Oracle 9i 中个数不能超过256,Oracle 10g个数不能超过1000。
当in的个数大于1000时,有以下两个解决办法:

  1. 对参数进行处理,分成多个in,其中每个in列表中参数都小于1000。如 params in(1,2,3.........1000) or params in(1001,1002...2000)。
    不过这种方法性能和维护性方面不好
  2. 将in后面的字符串改成子查询,将in里面的数据保存到临时表中,params in(select ....from dual)

三、SQL语句中过滤条件where和having的区别

  1. where 是一个约束声明,使用 where 约束来自数据库的数据,where 是在结果返回之前起作用,where 中不能使用聚合函数。
  2. Having 是一个过滤声明,是在查询返回结果集以后对查询结果进行的过滤操作,在 Having 中可以使用聚合函数。
  3. 在查询过程中,where 子句执行优先级高于聚合语句。聚合语句(sum,min,max,avg,count)优先级高于 having 子句。

四、一个表建有多个索引,Oracle如何选择

一个表最多可有255 个字段和32个索引。

  1. 表的主键、外键必须有索引;
  2. 数据量超过300的表应该有索引;
  3. 经常与其他表进行连接的表,在连接字段上应该建立索引;
  4. 经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;
  5. 索引应该建在选择性高的字段上;
  6. 索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;
  7. 复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:
    ①正确选择复合索引中的主列字段,一般是选择性较好的字段。
    ②复合索引的几个字段是否经常同时以 AND 方式出现在 Where 子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引。
    ③复合索引中包含的字段经常单独出现在 where 子句中,则分解为多个单字段索引。
    ④复合索引所包含的字段超过3个,要仔细考虑必要性,考虑减少复合的字段。
    ⑤既有单字段索引,又有这几个字段上的复合索引,可以删除复合索引。
  8. 频繁进行数据操作的表,不要建立太多的索引;
  9. 删除无用的索引,避免对执行计划造成负面影响;
    以上是一些普遍的建立索引时的判断依据。一言以蔽之,索引的建立必须慎重,对每个索引的必要性都应该经过仔细分析,要有建立的依据。因为太多的索引与不充分、不正确的索引对性能都毫无益处:在表上建立的每个索引都会增加存储开销,索引对于插入、删除、更新操作也会增加处理上的开销。另外,过多的复合索引,在有单字段索引的情况下,一般都是没有存在价值的;相反,还会降低数据增加删除时的性能,特别是对频繁更新的表来说,负面影响更大。

五、一张商品表,商品有类目属性,找出某个类目下最新创建的100个商品,表索引怎么设计?

  1. 类目加索引,需要遍历类目下所有商品,当类目商品数量多时,执行速度慢,不同类目的执行效率不一样。
  2. 创建时间加索引,执行过程会命中创建时间索引,按照时间倒排筛选类目,如果当前类目很长时间没有新品创建,有可能遍历整个表。
  3. 类目+创建时间联合索引,先命中类目索引,然后按照创建时间后续遍历就可以拿到最新的100条。

六、一个6亿的表A,一个3亿的表B,通过外键tid关联,如何最快查询出有关联关系的第50000到第50200中的这200条数据记录

1、如果 A 表 TID 是自增长,并且是连续的,B 表的 ID 为索引
select * from a,b where a.tid = b.id and a.tid>500000 limit 200;
2、如果 A 表的 TID 不是连续的,那么就需要使用覆盖索引。TID 要么是主键,要么是覆盖索引,B 表 ID 也需要有索引。
select * from b, (select tid from a limit 50000,200) a where b.id = a.tid;

总结:SQL调优方法很多,同样的查询结果可以有很多种不同的查询方式。其实最好的方法就是在开发环境中用最贴近真实的数据集和硬件环境进行测试,然后再发布到生产环境中。

你可能感兴趣的:(SQL语句优化)