sql怎么读_关于对于sql常见的错误理解

create table test3(id int not null PRIMARY KEY,

cust_name varchar2(10),

type1 varchar2(10) not null,

value1 varchar2(10),

value2 varchar2(10),

value3 varchar2(10),

value4 varchar2(10),

value5 varchar2(10),

value6 varchar2(10)

);

begin

for i in 1..10000 loop

insert into test3 values(i,'zry'||i,'aaaa','value1','value2','value3','value4','value5','value6');

end loop;

commit;

end;

/

begin

for i in 10001..11000 loop

insert into test3 values(i,'zry'||i,'bbbb','value1','value2','value3','value4','value5','value6');

end loop;

commit;

end;

/

begin

for i in 11001..12000 loop

insert into test3 values(i,'zry'||i,'cccc','value1','value2','value3','value4','value5','value6');

end loop;

commit;

end;

/

begin

for i in 12001..13000 loop

insert into test3 values(i,'zry'||i,'dddd','value1','value2','value3','value4','value5','value6');

end loop;

commit;

end;

/

begin

for i in 13001..14000 loop

insert into test3 values(i,'zry'||i,'eeee','value1','value2','value3','value4','value5','value6');

end loop;

commit;

end;

/

begin

for i in 14001..15000 loop

insert into test3 values(i,'zry'||i,'ffff','value1','value2','value3','value4','value5','value6');

end loop;

commit;

end;

/

insert into test3(id,type1,value1,value2,value3,value4,value5,value6) values(15001,'gggg','value1','value2','value3','value4','value5','value6');

在开始问题之前先介绍oracle几种看执行计划的方式,当然方式很多,为了不浪费时间我只介绍三种常用的吧,只有了解了执行计划才能知道sql是如何真正执行的

(1)第一种,explain plan的方式-这种方式sql没有真正执行,只是根据统计信息等优化器估算的一种执行计划,一般来说都是跟真正的执行计划是一样的。----也就是说这种不是真正的执行计划,虽然概率小,但是有可能有出入

SQL> explain plan for select * from test3 where id=10003;

9b2f2f837d4a883929d1165613251d6f.png

(2)第二种,这种方式的执行计划其实也是取自v$sql的基表,跟第一种是一样的,但是sql是执行过的,下方的statistics信息是正确的,这种方式可以看到真正的消耗,包括物理读、逻辑读等信息 ----这种方式看到的也不是真正的执行计划,但是可以看到真实的消耗

abb66e8df866fb02be3e7f36d718126f.png

(3)第三种,这种需要先执行sql才能看到,这种执行计划才是真正的执行计划,a-rows是真实的行数

SQL> ALTER SESSION SET STATISTICS_LEVEL=ALL;

f547d4faf5b88c336fc3190cf3393549.png

下面来介绍下通常听说到的错误言论

1.空值列可不可以建索引

SQL> create index idx_name1 on test3(cust_name);

Index created.

一般的索引是不保存null值,并不是不能在有null值的列上创建索引

SQL> select id,cust_name from test3 where cust_name='zry10004';

f54668782d720f9f54cf6fc2216a0404.png

3875ad85b3d01f62e6f4f88ed86ff306.png

2.where column is null能不能走索引,以及为什么有时我建了索引没有变快(1.索引不合理 2.索引失效或没有走索引)

这种说法是见的比较多的,甚至有的人直接说null,<>,like不能走索引。

①null值也可以走索引

SQL> create index idx_name2 on test3(cust_name,0);

通过这种方式可以让索引保存null值,下面可以看到图片中真实消耗的逻辑读降低很多,也走了索引,但是执行计划只有第三张的显示是完全正确的。但是基本信息都是对的。

b01b6fc3f9ef7209d55752927d129ac9.png

如果走索引是效率比较高的方式,但是建上索引没有走,是什么原因。很有可能是统计信息失效,可以先收集下统计信息,如下图所示

9b1f5f1eb89c33018cabc3a73259b165.png

e41a4a33b437fd5f0a8c1d30bcfbe7e5.png

②<>是不会走索引的

a0156f682dc1689b8e8cfc1228839af0.png

通过这张图可以看到是通过索引先筛选id再去回表筛选<>的条件,<>并没有走索引

③like 'x%'是可以走索引的,其他的'%x%'’和'%x'是不走索引的

SQL> create index idx_type1 on test3(type1);

9cfb6992779b96ba489dda79db6bd466.png

9b061434f3bc7bb35c2e5788bdf286aa.png

d166030c445f5a0b8b5176cbf572ae3d.png

3.消除回表(因为有人疑惑为什么我的sql走索引了啊,怎么出现效率问题了,有可能是回表造成的)

这儿很多朋友并不知道回表是什么意思,简单解释下,一条sql的where条件列有索引,并且筛选性不错,那么执行的时候一般都会走索引,但是查询出的列可能有好几列不在索引中,这时候通过索引筛选完,还要回到表中去取其他的数据,就造成了又一次消耗,称为回表。

有回表并不一定要去消除,有时在不修改sql语句的时候也无法消除。只有造成性能压力的时候才去考虑。

1acd38ee5a05c6cb044417ef279b74cc.png

最常用有效的方式就是建立联合索引

4b92f2f45ba15430e6850baef0d65e82.png

4.where后条件先后顺序,有人说where后面要先写筛选性高的条件,因为在前面的先执行(是有多看不起优化器啊)

SQL> create index idx_type on test3(type1,0);

Index created.

SQL> select * from test3 where type1='cccc' and id between 11001 and 11013;

dba80f23e5f9bb7ffe6318b61fea63ef.png

SQL> select * from test3 where id between 11001 and 11013 and type1='cccc' ;

4b14bd9074aa4f0d5e341cd25b83a0ca.png

可以看出执行计划一样,而且消耗也是一样,所以sql执行顺序跟where后条件位置无关,一般优化器会选择更高效的方式

强制走索引

select /*+ index(t idx_type)*/ * from test3 t where t.id between 11001 and 11013 and t.type1='cccc' ;

e7112ca8a0acce0575c1fdc06224dc19.png

可以看到强制走其他索引的消耗变高了

5. in 、exits 和join,not in、not exists和join

create table test4(id int not null PRIMARY KEY,

cust_name varchar2(10)

);

begin

for i in 1..1000 loop

insert into test4 values(i,'zry'||i);

end loop;

commit;

end;

/

①in 、exits 和join

b2649a7aba9ed27dd704ac58320c7512.png

8c01473dd002eea6f74ecdeb439d4b74.png

24f6cdbb0371da2564e9aeb6105e0d16.png

②not in、not exists和join

begin

for i in 1001..14999 loop

insert into test4 values(i,'zry'||i);

end loop;

commit;

end;

/

1c40bf27ac0abb365676228a4bcd75fc.png

dccf5fa3d29cb4cc8f72a4274a8d381b.png

538ac9b0f5ce7c4a1813e89788e392c2.png

可以看到in和exists、not in和not exists的执行计划一样,join的执行计划改变了,所以真正情况下谁的效率高低也没有确定的结论。要考虑多种情况,像上面这种执行计划一样的效率肯定一样,所以直接说not exists比not in效率高的都是错误的。

ps:生产中一般用not exists替带not in 在大数据量的处理中、和不同的数据库的优化器选择中,not exists确实效率会比not in高,但是一般exists和in的效率差不多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值