|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
这类操作的执行过程是,1)按照新的表定义建立一个临时表tmpa,2)将原表数据拷贝到临时表,3)将原始表改名tmpb,4)将tmpa改名为原表名,5)将tmpb删除。索引利用索引的误区之四:空值对索引的影响
我们起首做一些测试数据:
SQL>createtablet(xint,yint);
Tablecreated
请注重,这里我对表t做了一个独一(团结)索引:
SQL>createuniqueindext_idxont(x,y);
Indexcreated
SQL>insertintotvalues(1,1);
1rowinserted
SQL>insertintotvalues(1,NULL);
1rowinserted
SQL>insertintotvalues(NULL,1);
1rowinserted
SQL>insertintotvalues(NULL,NULL);
1rowinserted
SQL>commit;
Commitcomplete
上面我们剖析一下索引:
SQL>analyzeindext_idxvalidatestructure;
Indexanalyzed
SQL>selectname,lf_rowsfromindex_stats;
NAMELF_ROWS
----------------------------------------
T_IDX3
SQL>
然后,我们就能够看到,以后的索引中仅仅保留了3行数据。
请注重,下面我们拔出并提交了四行数据。
以是,这里就有一个结论:
Oracle的索引不保留全体为空的行。
我们持续拔出数据,如今再拔出几行全体为空的行:
SQL>insertintotvalues(NULL,NULL);
1rowinserted
SQL>insertintotvalues(NULL,NULL);
1rowinserted
我们看到如许的拔出,竟然没有违背后面我们设定的独一束缚(uniqueont(x,y)),
以是,这里我们又得出一个结论:
Oracle以为NULLNULL,进而(NULL,NULL)(NULL,NULL)
换句话说,Oracle以为空值(NULL)不即是任何值,包含空值也不即是空值。
我们看到上面的拔出会违背独一束缚(DEMO.T_IDX),这个很好了解了,由于它不是全体为空的值,即它不是(NULL,NULL),只要全体为空的行才被以为是分歧的行:
SQL>insertintotvalues(1,null);
insertintotvalues(1,null)
ORA-00001:违背独一束缚前提(DEMO.T_IDX)
SQL>insertintotvalues(null,1);
insertintotvalues(null,1)
ORA-00001:违背独一束缚前提(DEMO.T_IDX)
SQL>
请看上面的例子:
SQL>selectx,y,count(*)fromtgroupbyx,y;
XYCOUNT(*)
-----------------------
3
11
11
111
Executedin0.03seconds
SQL>selectx,y,count(*)fromtwherexisnullandyisnullgroupbyx,y;
XYCOUNT(*)
---------------------
3
Executedin0.01seconds
SQL>
SQL>selectx,y,count(*)fromtgroupbyx,yhavingcount(*)>1;
XYCOUNT(*)
------------------------------------
3
Executedin0.02seconds
SQL>
能够瞥见,完整为空的行有三行,这里我们又能够得出一个结论:
oracle在groupby子句中以为完整为空的行是不异的行
换句话说,在groupby子句中,oracle以为(NULL,NULL)=(NULL,NULL)
上面的语句,利用了复合索引(x,y)的前导列,一般如许的查询是会利用索引的,我们看看上面的例子:
select*fromtwherexisnull;
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------
|Id|Operation|Name|Rows|Bytes|Cost|
--------------------------------------------------------------------
|0|SELECTSTATEMENT|||||
|*1|TABLEACCESSFULL|T||||
--------------------------------------------------------------------
PredicateInformation(identifiedbyoperationid):
---------------------------------------------------
1-filter("T"."X"ISNULL)
Note:rulebasedoptimization
14rowsselected
Executedin0.06seconds
我们看到下面的查询并没有利用索引,那末对照一下不利用把持的情形:
对照一下上面的查询:
select*fromtwherex=1;
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------
|Id|Operation|Name|Rows|Bytes|Cost|
--------------------------------------------------------------------
|0|SELECTSTATEMENT|||||
|*1|INDEXRANGESCAN|T_IDX||||
--------------------------------------------------------------------
PredicateInformation(identifiedbyoperationid):
---------------------------------------------------
1-access("T"."X"=1)
Note:rulebasedoptimization
14rowsselected
Executedin0.04seconds
这个查询(wherex=1)如我们所但愿的那样利用了t_idx(x,y)复合索引,这里我们能够得出一个结论:
在利用ISNULL和ISNOTNULL前提的时分,Oracle不利用索引(由于Oracle的索引不存储空值,具体请拜见后面的相干内容)
那末我们怎样利用空值的对照前提呢?
起首,只管不在前导列上利用空值,请看上面的例子:
select*fromtwherex=1andyisnull;
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------
|Id|Operation|Name|Rows|Bytes|Cost|
--------------------------------------------------------------------
|0|SELECTSTATEMENT|||||
|*1|INDEXRANGESCAN|T_IDX||||
--------------------------------------------------------------------
PredicateInformation(identifiedbyoperationid):
---------------------------------------------------
1-access("T"."X"=1)
filter("T"."Y"ISNULL)
Note:rulebasedoptimization
15rowsselected
select*fromtwherexisnullandy=1;
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------
|Id|Operation|Name|Rows|Bytes|Cost|
--------------------------------------------------------------------
|0|SELECTSTATEMENT|||||
|*1|TABLEACCESSFULL|T||||
--------------------------------------------------------------------
PredicateInformation(identifiedbyoperationid):
---------------------------------------------------
1-filter("T"."Y"=1AND"T"."X"ISNULL)
Note:rulebasedoptimization
14rowsselected
另有一个能够变通的办法,即我们在创立表的时分,为每一个列都指定为非空束缚(NOTNULL),而且在需要的列上利用default值,如:
SQL>createtablelunar(
2c1varchar2(10)defaultempty
3constraintc1_notnullnotnull,
4c2number(10)default0
5constraintc2_notnullnotnull,
6c3datedefaultto_date(20990101,yyyymmdd)
7constraintc3_notnullnotnull);
表已创立。
已用工夫:00:00:00.00
SQL>insertintolunar(c1)values(first);
已创立1行。
已用工夫:00:00:00.00
SQL>insertintolunar(c2)values(99);
已创立1行。
已用工夫:00:00:00.00
SQL>insertintolunar(c3)values(sysdate);
已创立1行。
已用工夫:00:00:00.00
SQL>insertintolunar(c1,c3)values(ok,sysdate);
已创立1行。
已用工夫:00:00:00.00
SQL>insertintolunar(c2,c1)values(999,hello);
已创立1行。
已用工夫:00:00:00.00
SQL>commit;
提交完成。
已用工夫:00:00:00.00
SQL>select*fromlunar;
C1C2C3
------------------------------
first001-1月-99
empty9901-1月-99
empty019-10月-04
ok019-10月-04
hello99901-1月-99
已用工夫:00:00:00.00
SQL>selectc1,c2,to_char(c3,yyyy-mm-yy)fromlunar;
C1C2TO_CHAR(C3
------------------------------
first02099-01-99
empty992099-01-99
empty02004-10-04
ok02004-10-04
hello9992099-01-99
已用工夫:00:00:00.00
SQL>
然后我们再像利用一样平常的列那样,利用他们,而且公道的为他们创建索引信任就能够很好的进步使用的查询效力。
在需要把许多新记录插入某个数据表的场合,DELAY_KEY_WRITE选项的作用将非常明显。另外,索引还会在硬盘上占用相当大的空间。因此应该只为最经常查询和最经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。 |
|