第1章 SQL优化必懂概念
1.1 基数(CARDINALITY)
某个列唯一键(Distinct_Keys)的数量叫作基数。比如性别列,该列只有男女之分,所以这一列基数是2。主键列的基数等于表的总行数。基数的高低影响列的数据分布。
以测试表test为例,owner列和object_id列的基数分别如下所示。
SQL> select count(distinct owner),count(distinct object_id),count(*) from test; COUNT(DISTINCTOWNER) COUNT(DISTINCTOBJECT_ID) COUNT(*) -------------------- ------------------------ ---------- 29 72462 72462
TEST表的总行数为72 462,owner列的基数为29,说明owner列里面有大量重复值,object_id列的基数等于总行数,说明object_id列没有重复值,相当于主键。owner列的数据分布如下。
SQL> select owner,count(*) from test group by owner order by 2 desc; OWNER COUNT(*) -------------------- ---------- SYS 30808 PUBLIC 27699 SYSMAN 3491 ORDSYS 2532 APEX_030200 2406 MDSYS 1509 XDB 844 OLAPSYS 719 SYSTEM 529 CTXSYS 366 WMSYS 316 EXFSYS 310 SH 306 ORDDATA 248 OE 127 DBSNMP 57 IX 55 HR 34 PM 27 FLOWS_FILES 12 OWBSYS_AUDIT 12 ORDPLUGINS 10 OUTLN 9 BI 8 SI_INFORMTN_SCHEMA 8 ORACLE_OCM 8 SCOTT 7 APPQOSSYS 3 OWBSYS 2
owner列的数据分布极不均衡,我们运行如下SQL。
select * from test where owner='SYS';
SYS有30 808条数据,从72 462条数据里面查询30 808条数据,也就是说要返回表中42.5%的数据。
SQL> select 30808/72462*100 "Percent" from dual; Percent ---------- 42.5160774
那么请思考,你认为以上查询应该使用索引吗?现在我们换一种查询语句。
select * from test where owner='SCOTT';
SCOTT有7条数据,从72 462条数据里面查询7条数据,也就是说要返回表中0.009%的数据。
SQL> select 7/72462*100 "Percent" from dual; Percent ---------- .009660236
请思考,返回表中0.009%的数据应不应该走索引?
如果你还不懂索引,没关系,后面的章节我们会详细介绍。如果你回答不了上面的问题,我们先提醒一下。当查询结果是返回表中5%以内的数据时,应该走索引;当查询结果返回的是超过表中5%的数据时,应该走全表扫描。
当然了,返回表中5%以内的数据走索引,返回超过5%的数据就使用全表扫描,这个结论太绝对了,因为你还没掌握后面章节的知识,这里暂且记住5%这个界限就行。我们之所以在这里讲5%,是怕一些初学者不知道上面问题的答案而纠结。
现在有如下查询语句。
select * from test where owner=:B1;
语句中,“:B1”是绑定变量,可以传入任意值,该查询可能走索引也可能走全表扫描。
现在得到一个结论:如果某个列基数很低,该列数据分布就会非常不均衡,由于该列数据分布不均衡,会导致SQL查询可能走索引,也可能走全表扫描。在做SQL优化的时候,如果怀疑列数据分布不均衡,我们可以使用select列,count(*) from表group by列order by 2 desc来查看列的数据分布。
如果SQL语句是单表访问,那么可能走索引,可能走全表扫描,也可能走物化视图扫描。在不考虑有物化视图的情况下,单表访问要么走索引,要么走全表扫描。现在,回忆一下走索引的条件:返回表中5%以内的数据走索引,超过5%的时候走全表扫描。相信大家读到这里,已经搞懂了单表访问的优化方法。
我们来看如下查询。
select * from test where object_id=:B1;
不管object_id传入任何值,都应该走索引。
我们再思考如下查询语句。
select * from test where object_name=:B1;
不管给object_name传入任何值,请问该查询应该走索引吗?
请你去查看object_name的数据分布。写到这里,其实有点想把本节名称改为“数据分布”。大家在以后的工作中一定要注意列的数据分布!