oracle 为什么我们需要主键或unique来创建位图索引

f0brbegy  于 2023-01-08  发布在  Oracle
关注(0)|答案(1)|浏览(130)

我尝试创建位图索引为

CREATE BITMAP INDEX ...
ON ...(...)
FROM d1, d2
WHERE d1.AIRCRAFTID = d2.ID PCTFREE 0;

其中表没有主键也没有唯一值。执行后,我得到了错误ORA-25954。修改表并使用索引创建唯一约束,它工作得很好。
那么,我们为什么要这样做呢?

gopyfrb3

gopyfrb31#

位图联接索引需要主键或维列上的唯一约束条件,因为重复行会显著改变位图的工作方式。允许重复会导致数据结构效率大大降低,在极少数情况下需要大量代码更改,或者两者兼而有之。(由于我们无法访问Oracle的源代码,这个答案大多是推测,但基于位图索引的工作方式,我认为这些猜测是合理的。)
首先,下面的示例演示了如何创建和使用部分基于this Oracle-base article的简单位图连接索引。(通常很大),以及一个CUSTOMER维度表位图索引预联接这两个表,并将CUSTOMER.STATE值作为隐藏列SYS_NC00004$存储在SALES表中。位图联接索引允许联接表的查询使用位图索引,并且在运行时不访问或联接CUSTOMER表。当解释计划中的BITMAP INDEX SINGLE VALUE操作使用索引IDX_CUSTOMTER_SALES而不是CUSTOMER表时,我们可以看到这种情况。

--drop table customer;
--drop table sales;

create table sales(sales_id number primary key, dollar_amount number not null, cust_id number not null);
insert into sales values(1, 100, 1);
insert into sales values(2, 200, 2);

create table customer(cust_id number primary key, state varchar2(100) not null);
insert into customer values(1, 'CA');
insert into customer values(2, 'NY');

create bitmap index idx_customter_sales
on    sales(customer.state)
from  sales, customer
where sales.cust_id = customer.cust_id;

explain plan for
select /*+ index(sales idx_customter_sales) */ sum(sales.dollar_amount)
from   sales,
       customer
where  sales.cust_id  = customer.cust_id
and    customer.state = 'CA';

select * from table(dbms_xplan.display);

--Plan:
Plan hash value: 477564610
 
------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name                | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                     |     1 |    26 |     6   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE                      |                     |     1 |    26 |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| SALES               |     1 |    26 |     6   (0)| 00:00:01 |
|   3 |    BITMAP CONVERSION TO ROWIDS       |                     |       |       |            |          |
|*  4 |     BITMAP INDEX SINGLE VALUE        | IDX_CUSTOMTER_SALES |       |       |            |          |
------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - access("SALES"."SYS_NC00004$"='CA')
 
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)

位图索引为每个不同的值创建一个新的位图。使用ROWID(物理地址),Oracle只需使用一个位就可以确定每个值是否与每个表行匹配。ROWID是内部物理结构,我们不知道它们是如何工作的,但可以肯定的是,ROWID是有序的,Oracle不必为每一行列出ROWID。索引压缩行号,可能使用一些元数据来指示“这个长的位列表用于ROWID范围AAAftAAAMAAAK 0 kAAA到AAAftAAAMAAAK 0 kZZZ”
从逻辑上讲,位图看起来像这样:

CUSTOMER.STATE   bitmap for SALES ROWID AAAftAAAMAAAK0kAAA - AAAftAAAMAAAK0kZZZ
--------------   --------------------------------------------------------------
CA               10...
NY               01...

每个位图非常小,因为每个表行只需要1位加上少量开销。如果还有其他列的位图,Oracle可以将AND predicate 作为简单的布尔CPU AND来执行,这是超级快的。但是,如果列不是非常唯一,并且有许多位图,那么索引将是巨大的,效率低下。
添加重复的维行会破坏一切。例如,假设我们为加州添加了重复的客户:

insert into customer values(3, 'CA');

现在,两个表之间的连接可能返回重复项--SALES中的一行可能与CUSTOMER中的多行匹配。Oracle通常可以轻松地处理这种情况,但我不确定它是否可以使用位图索引来处理这种情况。简单的0或1不再是足够的信息--数据库还需要知道有多少重复项。现在,我们存储计数而不是位,如下所示:

CUSTOMER.STATE   count(?) map for SALES ROWID AAAftAAAMAAAK0kAAA - AAAftAAAMAAAK0kZZZ
--------------   --------------------------------------------------------------
CA               20...
NY               01...

现在数据结构已经从位变成了字节,如果有大量重复数据,甚至可能变成多个字节,位图连接索引的大小现在至少增加了8倍,它们不能再用于快速的二进制数学运算。
允许重复消除了位图连接索引的所有优点。而且这可能是一个非常罕见的用例。我不确定在事实表和维度表之间建立一对多关系是否有意义。

相关问题