且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

[20140512]关于降序索引.txt

更新时间:2021-09-10 15:29:06

[20140512]关于降序索引.txt

https://jonathanlewis.wordpress.com/2014/05/07/quiz-night-23/

提到建立降序索引,会出现建立唯一索引出现促错误的问题,自己做一个测试了解一些细节:

1.测试环境:

SCOTT@test> @ver

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

create table t (id number,name varchar2(10));
insert into t values (1,'aaaaaa');
insert into t values (2,'bbbbbb');
insert into t values (null,null);
insert into t values (null,null);
commit ;

2.可以发现建立唯一索引,但是如果使用降序索引就不行。

SCOTT@test> create unique index i_t_id on t(id);
Index created.

SCOTT@test> create unique index i_t_id on t(id desc);
create unique index i_t_id on t(id desc)
                                *
ERROR at line 1:
ORA-01452: cannot CREATE UNIQUE INDEX; duplicate keys found

--可以发现建立降序索引失败。

3.参见链接:https://jonathanlewis.wordpress.com/2014/05/07/quiz-night-23/
Answer

Well it didn't take long for an answer and several bits of related infomration to show up – as Martin pointed out, all
I have to do is insert NULL into the table twice.

To create an entry in a descending index, Oracle takes the 1's-complement of each column and appends an 0xFF byte to
each column – except in the case of a null column where the null is replaced with a 0x00. (And, as Sayan points out,
funny things happen if you have a varchar2() column which has already reached the 4,000 byte limit)

The point of the 1's-complement is that if you walk through the stored values in ascending order you're walking through
the original values in descending – provided you have the 0xFF on the end of each non-null entry.

4.看看降序索引是如何保存的.
SCOTT@test> create  index i_t_id on t(id desc);
Index created.

SCOTT@test> create  index i_t_name on t(name desc);
Index created.

SCOTT@test> select segment_name,header_file,header_block from dba_segments where owner=user and segment_name in ('I_T_ID','I_T_NAME');
SEGMENT_NAME         HEADER_FILE HEADER_BLOCK
-------------------- ----------- ------------
I_T_NAME                       4         1426
I_T_ID                         4          634

--记录很少,仅仅只有一个索引根节点。

SCOTT@test> alter system dump datafile 4 block 635;
System altered.

SCOTT@test> alter system dump datafile 4 block 1427;
System altered.


--看看里面保存的信息:
Block header dump:  0x0100027b
Object id on Block? Y
seg/obj: 0x45d9b  csc: 0x00.c2f0184a  itc: 2  flg: E  typ: 2 - INDEX
     brn: 0  bdba: 0x1000278 ver: 0x01 opc: 0
     inc: 0  exflg: 0

Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000
0x02   0xffff.000.00000000  0x00000000.0000.00  C---    0  scn 0x0000.c2f0184a
Leaf block dump
===============
header address 182927221348=0x2a974fe264
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 4
kdxcofbo 44=0x2c
kdxcofeo 7984=0x1f30
kdxcoavs 7940
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8032
row#0[8021] flag: ------, lock: 0, len=11
col 0; len 1; (1):  00
col 1; len 6; (6):  01 00 02 73 00 02
row#1[8010] flag: ------, lock: 0, len=11
col 0; len 1; (1):  00
col 1; len 6; (6):  01 00 02 73 00 03
row#2[7997] flag: ------, lock: 0, len=13
col 0; len 3; (3):  3e fc ff
col 1; len 6; (6):  01 00 02 73 00 01
row#3[7984] flag: ------, lock: 0, len=13
col 0; len 3; (3):  3e fd ff
col 1; len 6; (6):  01 00 02 73 00 00
----- end of leaf block dump -----
End dump data blocks tsn: 4 file#: 4 minblk 635 maxblk 635


Block header dump:  0x01000593
Object id on Block? Y
seg/obj: 0x45d9c  csc: 0x00.c2f0189c  itc: 2  flg: E  typ: 2 - INDEX
     brn: 0  bdba: 0x1000590 ver: 0x01 opc: 0
     inc: 0  exflg: 0

Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   0x0000.000.00000000  0x00000000.0000.00  ----    0  fsc 0x0000.00000000
0x02   0xffff.000.00000000  0x00000000.0000.00  C---    0  scn 0x0000.c2f0189c
Leaf block dump
===============
header address 182927221348=0x2a974fe264
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 2
kdxcosdc 0
kdxconro 4
kdxcofbo 44=0x2c
kdxcofeo 7976=0x1f28
kdxcoavs 7932
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8032
row#0[8021] flag: ------, lock: 0, len=11
col 0; len 1; (1):  00
col 1; len 6; (6):  01 00 02 73 00 02
row#1[8010] flag: ------, lock: 0, len=11
col 0; len 1; (1):  00
col 1; len 6; (6):  01 00 02 73 00 03
row#2[7993] flag: ------, lock: 0, len=17
col 0; len 7; (7):  9d 9d 9d 9d 9d 9d ff
col 1; len 6; (6):  01 00 02 73 00 01
row#3[7976] flag: ------, lock: 0, len=17
col 0; len 7; (7):  9e 9e 9e 9e 9e 9e ff
col 1; len 6; (6):  01 00 02 73 00 00
----- end of leaf block dump -----
End dump data blocks tsn: 4 file#: 4 minblk 1427 maxblk 1427

---可以发现NULL无论number还是varchar2,都是00,在降序索引里面。

SCOTT@test> select dump('aaaaaa',16),dump('bbbbbb',16) from dual ;
DUMP('AAAAAA',16)               DUMP('BBBBBB',16)
------------------------------- -------------------------------
Typ=96 Len=6: 61,61,61,61,61,61 Typ=96 Len=6: 62,62,62,62,62,62

SCOTT@test> @16to10 61
16 to 10 DEC
------------
          97

SCOTT@test> @16to10 9e
16 to 10 DEC
------------
         158

SCOTT@test> select 97+158 from dual ;
    97+158
----------
       255

--可以发现相当于使用0xff与name异或,然后在后面加0xff.看看数字:
row#3[7984] flag: ------, lock: 0, len=13
col 0; len 3; (3):  3e fd ff
col 1; len 6; (6):  01 00 02 73 00 00

SCOTT@test> select dump(1,16),dump(1,10),dump(2,16),dump(2,10) from dual ;
DUMP(1,16)        DUMP(1,10)         DUMP(2,16)        DUMP(2,10)
----------------- ------------------ ----------------- ------------------
Typ=2 Len=2: c1,2 Typ=2 Len=2: 193,2 Typ=2 Len=2: c1,3 Typ=2 Len=2: 193,3

SCOTT@test> @16to10 3e
16 to 10 DEC
------------
          62

SCOTT@test> @16to10 fd
16 to 10 DEC
------------
         253

-- 62+193=255
-- 2+253=255

-- 可以发现数字性也是一样,使用0xff与id异或,然后在后面加0xff.

5.虽然降序索引是这样建立,但是如果这样查询依旧不会使用。
SCOTT@test> execute dbms_stats.gather_table_stats(user,'t',cascade=>true);
PL/SQL procedure successfully completed.

SCOTT@test> select /*+ index(t,i_t_id) */ count(*) from t ;
  COUNT(*)
----------
         4

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-----------------------------------------------------------
SQL_ID  92v6cqgrkpkw2, child number 0
-------------------------------------
select /*+ index(t,i_t_id) */ count(*) from t

Plan hash value: 2966233522

---------------------------------------------------------
| Id  | Operation          | Name | E-Rows | Cost (%CPU)|
---------------------------------------------------------
|   0 | SELECT STATEMENT   |      |        |     3 (100)|
|   1 |  SORT AGGREGATE    |      |      1 |            |
|   2 |   TABLE ACCESS FULL| T    |      4 |     3   (0)|
---------------------------------------------------------

--建立函数索引看看。

SCOTT@test> create index if_t_id on t(id,0);
Index created.

SCOTT@test> select /*+ index(t,if_t_id) */ count(*) from t ;
  COUNT(*)
----------
         4

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  2tawnyjhj1ufb, child number 0
-------------------------------------
select /*+ index(t,if_t_id) */ count(*) from t
Plan hash value: 428201273
----------------------------------------------------------
| Id  | Operation        | Name    | E-Rows | Cost (%CPU)|
----------------------------------------------------------
|   0 | SELECT STATEMENT |         |        |     1 (100)|
|   1 |  SORT AGGREGATE  |         |      1 |            |
|   2 |   INDEX FULL SCAN| IF_T_ID |      4 |     1   (0)|
----------------------------------------------------------