虎虎漫画小说

繁体版 简体版
虎虎漫画小说 > > SQL语言艺术最新章节 > 第 10 章

第 10 章 免费阅读

意为此设置了存储选项参数,就更是如此了。

现在做一个假定:键值与特定chā入环境无关、与存储设置无关,与键值(或键值范围)对应的

记录可能存储在磁盘的任何位置。索引仅以特定顺序来存储键值,而对应的记录随机散落在表

中。此时,若既不分区、也不采用聚集索引,则需访问的存储区会更多。于是,可能出现下列

情况:同一个表上有两个可选择xìng完全相同的索引,但一个索引xìng能好、一个索引xìng能差。这

种情况在第3章已提到过,下面来分析一下。

为了说明上述情况,先创建一个具有1 000 000条记录的表,这个表有c1、c2和c3 三个字段,

c1 保存序号(1 到1 000 000), c2 保存从1 到2 000 000 不等的随机数,c3 保存可重复、

且经常重复的随机值。表面看来,c1 和c2 都具唯一xìng,因此具有完全相同的可选择xìng。索引

建在c1上,则表中字段的顺序,与索引中的顺序相符——当然,实际上,对表的删除cāo作会留

下“空洞”,随后又有新的chā入记录填入,所以记录顺序会被打乱。相比之下,索引建在c2上,

则表中记录顺序与索引中的顺序无关。

下面读取c3 ,使用如下范围条件:

where column_ncom between scom_value and scom_value + 10

如图6-1所示,使用c1索引(有序索引,索引中键的顺序与表中记录顺序相同)和c2索引(随机

索引)的xìng能差异很大。别忘了造成这种差异的原因:为了读取c3的值,除了访问索引,还要

访问表。如果我们有两个复合索引,分别在(c1, c3) 和(c2, c3) 上,就不会有上述差异了,因

为这时不必访问表,从索引中即可获得要返回的内容。

图6-1说明的这种xìng能差异,也解释了下述情况的原因:有时xìng能会随时间而降低,尤其是在新

系统刚投入生产环境并导入旧系统的大量数据时。最初加载的数据的物理排序,可能是有利于

特定查询的;但随后几个月的各种活动破坏了这种顺序,于是xìng能“神秘”降低30%~40%。

图6-1:“索引项顺序与表中记录顺序是否一致”对xìng能的影响

现在很清楚了,“DBA可以随时重新组织数据库”其实是错误的。数据库的重新组织曾一度流行;

但不断增加的数据量及99 999 9% 正常运行等要求,使得重新组织数据库变得不再适合。如果

物理存储方式很重要,则应考虑第5章讨论过的“自组织结构(self-organizing structure)”之一,

例如聚集索引(clustered indexe)或索引组织表(index-organized table)。但要记住,对某种类

型的查询有利,可能对另一种类型的查询不利,鱼与熊掌不可得兼。

总结:类似的索引,xìng能却不同,这可能是物理数据的散布引起的。

条件的“

可索引xìng”

Criterion Indexability

对“小结果集,直接条件”的情况而言,适当的索引非常重要。但是,其中也有不适合加索引的

例外情况:以下案例,用来判断会计账目是否存在“金额不平”的情况,虽然可选择xìng很高,但

不适合加索引。

此例中,有个表glreport,该表包含一个应为0的字段amount_diff。此查询的目的是要追踪会计错

误,并找出amount_diff不是0的记录。既然使用了现代的DBMS,直接把账目对应成表,并应用

从前“纸笔记账”的逻辑,实在有点问题;但很不幸,我们经常遇到这种有问题的数据库。无论

设计的质量如何,像amount_diff这样的字段

通常不应加索引,因为在理想情况下每条记录的amount_diff字段都是0。此外,amount_diff字

段明显是“非规范化”设计的结果,大量计算要cāo作该字段。维护一个计算字段上的索引,代价

要高于静态字段上的索引,因为被修改的键会在索引内“移动”,于是索引要承受的开销比简单

节点增/删要高。

总结:并非所有明确的条件都适合加索引。特别是,频繁更新的字段会增加索引维护的成本。

回到例子。开发者有天来找我,说他已最佳化了以下Oracle 查询,并询问过专家建议:

select

total.deptnum,

total.accounting_period,

total.ledger,

total.cnt,

error.err_cnt,

cpt_error.bad_acct_count

from

-- First in-line view

(select

deptnum,

accounting_period,

ledger,

count(account) cnt

from

glreport

group by

deptnum,

ledger,

accounting_period) total,

-- Second in-line view

(select

deptnum,

accounting_period,

ledger,

count(account) err_cnt

from

glreport

where

amount_diff <> 0

group by

deptnum,

ledger,

accounting_period) error,

-- Third in-line view

(select

deptnum,

accounting_period,

ledger,

count(distinct account) bad_acct_count

from

glreport

where

amount_diff <> 0

group by

deptnum,

ledger,

accounting_period

) cpt_error

where

total.deptnum = error.deptnum(+) and

total.accounting_period = error.accounting_period(+) and

total.ledger = error.ledger(+) and

total.deptnum = cpt_error.deptnum(+) and

total.accounting_period = cpt_error.accounting_period(+) and

total.ledger = cpt_error.ledger(+)

order by

total.deptnum,

total.accounting_period,

total.ledger

外层查询where子句中的“(+)”是Oracle 特有的语法,代表外连接(outer join)。换言之:

select whatever

from ta,

tb

where ta.id = tb.id (+)

相当于:

select whatever

from ta

outer join tb

on tb.id = ta.id

下列SQL*Plus输出显示了该查询的执行计划:

10:16:57 SQL> set autotrace traceonly

10:17:02 SQL> /

37 rows selected.

Elapsed: 00:30:00.06

Execution Plan

----------------------------------------------------------

『加入书签,方便阅读』