介紹B樹索引

B樹索引

簡介

B樹索引是Oracle默認索引,B樹索引能夠提升SQL語句的性能,強制執行主鍵和惟一鍵約束的惟一性,減小經過主鍵和外鍵約束關聯的父表和子表間潛在都鎖定問題。數據庫

建立索引步驟

  • 估算索引大小
  • 指定表空間
  • 容許對象從他們的表空間繼承存儲參數
  • 定義建立索引要使用的命名標準

索引的建立

建立數據表空間

create tablespace reporting_data
  datafile '+DATA/reporting_data01.dbf'
  size 1G
  extent management local
  uniform size 1M
  segment space management auto;

建立索引表空間

create tablespace reporting_index
  datafile '+DATA/reporting_index01.dbf'
  size 500M
  extent management local
  uniform size 128K
  segment space management auto
  nologging;

建立表,並指定表空間

CREATE TABLE cust(
 cust_id    NUMBER
,last_name  VARCHAR2(30)
,first_name VARCHAR2(30))
TABLESPACE reporting_data;

建立索引並指定索引表空間

create index cust_idx1 on cust(last_name) tablespace reporting_index online;

對於新建立的表,進行一次統計信息收集

SQL> exec dbms_stats.gather_table_stats(ownname=>'testidx',tabname=>'CUST',cascade=>true);

PL/SQL procedure successfully completed.

配置主鍵

ALTER TABLE cust ADD CONSTRAINT cust_pk PRIMARY KEY (cust_id) USING INDEX TABLESPACE reporting_index;

配置惟一性約束

ALTER TABLE cust ADD CONSTRAINT cust_uk1 UNIQUE (last_name, first_name) USING INDEX TABLESPACE reporting_index;

建立地址表,並指定表空間

CREATE TABLE address(
 address_id NUMBER
,cust_id    NUMBER
,street     VARCHAR2(30)
,city       VARCHAR2(30)
,state      VARCHAR2(30))
TABLESPACE reporting_data;

給地址表添加外鍵約束,外鍵索引對應cust表的cust_id列

ALTER TABLE address ADD CONSTRAINT addr_fk1 FOREIGN KEY (cust_id) REFERENCES cust(cust_id);

建立address表的外鍵索引

CREATE INDEX addr_fk1 ON address(cust_id) TABLESPACE reporting_index;

報告索引

set linesize 300
col index_name for a30
col INDEX_TYPE for a10
col TABLE_NAME for a20
col TABLESPACE_NAME for a30
col status for a20

select index_name, index_type, table_name, tablespace_name, status
from user_indexes
where table_name in ('CUST','ADDRESS');

INDEX_NAME                     INDEX_TYPE TABLE_NAME                     TABLESPACE_NAME                                             STATUS
------------------------------ ---------- ------------------------------ ----------------------------------------------------------- ------------------------
CUST_IDX1                      NORMAL     CUST                           REPORTING_INDEX                                             VALID
CUST_PK                        NORMAL     CUST                           REPORTING_INDEX                                             VALID
CUST_UK1                       NORMAL     CUST                           REPORTING_INDEX                                             VALID
ADDR_FK1                       NORMAL     ADDRESS                        REPORTING_INDEX                                             VALID
col index_name for a30
col column_name for a30
col column_position for 99999999999999
select index_name, column_name, column_position
from user_ind_columns
where table_name in ('CUST','ADDRESS')
order by index_name, column_position;

SQL> col index_name for a30
SQL> col column_name for a30
SQL> col column_position for 99999999999999
SQL> select index_name, column_name, column_position
  2  from user_ind_columns
  3  where table_name in ('CUST','ADDRESS')
  4  order by index_name, column_position;

INDEX_NAME                     COLUMN_NAME                    COLUMN_POSITION
------------------------------ ------------------------------ ---------------
ADDR_FK1                       CUST_ID                                      1
CUST_IDX1                      LAST_NAME                                    1
CUST_PK                        CUST_ID                                      1
CUST_UK1                       LAST_NAME                                    1
CUST_UK1                       FIRST_NAME                                   2
col segment_name for a30
col segment_type for a30
col extents for 999999999999
col bytes for 999999999999

select a.segment_name, a.segment_type, a.extents, a.bytes
from user_segments a, user_indexes  b
where a.segment_name = b.index_name
and   b.table_name in ('CUST','ADDRESS');

SQL> col segment_name for a30
SQL> col segment_type for a30
SQL> col extents for 999999999999
SQL> col bytes for 999999999999
SQL> 
SQL> select a.segment_name, a.segment_type, a.extents, a.bytes
  2  from user_segments a, user_indexes  b
  3  where a.segment_name = b.index_name
  4  and   b.table_name in ('CUST','ADDRESS');

SEGMENT_NAME                   SEGMENT_TYPE                         EXTENTS         BYTES
------------------------------ ------------------------------ ------------- -------------
CUST_IDX1                      INDEX                                      1        131072
CUST_PK                        INDEX                                      1        131072
CUST_UK1                       INDEX                                      1        131072

索引場景簡介

當向表中插入行時,Oracle將分配由無路數據庫塊組成的區,Oracle還將爲索引分配塊,對於每一個插入到表中的記錄,Oracle還將建立一個包含Rowid和列值的索引條目。
每一個索引項的rowid指向存儲該表的列值的數據文件和塊。ide

當從一個表及其對應的索引選擇數據時,有三種狀況性能

  • SQL查詢所須要的全部表的數據都在索引中,所以只須要訪問索引塊,不須要從表中讀取塊。
  • 查詢所需的全部信息沒有都包含在索引塊中,所以查詢優化器選擇既訪問索引塊也訪問表塊來檢索須要的數據,以知足查詢的結果。
  • 查詢優化器選擇不訪問索引。所以只訪問表塊。

場景一:全部數據位於索引塊。

有兩種狀況,在每種狀況下,執行查詢須要的全部數據,包括返回給用戶的數據,以及在where字句中被評估的數據,都位於該索引。
- 索引範圍掃描(index range scan) :若是優化器肯定它使用索引結構檢索查詢所需的多個行是有效的,那麼就使用這種索引。 索引範圍掃描被普遍應用在各類各樣的狀況
- 索引快速全掃描(index fast full scan)若是優化器肯定表中大部分行所須要進行檢索,那麼就使用這種掃描。可是全部須要的信息都存儲在索引中。因爲索引結構一般比表結構小,優化器肯定全索引掃描(比全表掃描)更高效。這種狀況對於統計(count)值的查詢比較常見

演示1 索引範圍掃描

場景二: 索引中不包含全部信息

場景三: 只有表被訪問信息

在建立前估算索引表大小

以下代碼估算了CUST表的First_name列上建立索引的大小優化

set serverout on
exec dbms_stats.gather_table_stats(user,'CUST');
variable used_bytes number
variable alloc_bytes number
exec dbms_space.create_index_cost('create index cust_idx2 on cust(first_name)',:used_bytes,:alloc_bytes);

print :used_bytes

print :alloc_bytes

SQL> conn testidx/"admin@123"   
Connected.
SQL> set serverout on
exec dbms_stats.gather_table_stats(user,'CUST');SQL> 

PL/SQL procedure successfully completed.

SQL> variable used_bytes number
SQL> variable alloc_bytes number
SQL> exec dbms_space.create_index_cost('create index cust_idx2 on cust(first_name)',:used_bytes,:alloc_bytes);

PL/SQL procedure successfully completed.

SQL> print :used_bytes

USED_BYTES
----------
        25

SQL> print :alloc_bytes

ALLOC_BYTES
-----------
      65536

SQL> 

used_bytes變量給出了索引數據須要多少空間的估算
alloc_bytes變量提供了將在表空間分配多大空間的估算。

建立索引

create index cust_idx2 on cust(first_name);
select bytes from user_segments where segment_name='CUST_IDX2';

注意:隨着數據的增長,索引將增大。必須對索引佔用的空間進行監控,確保有足夠的空間,來適應將來的增加需求。

顯示建立索引的代碼

* 如下語句現實了從新建立索引須要的全部方面的語句,這些代碼中的許多值反映了表空間繼承的默認設置和存儲參數。

SQL> set long 10000000
SQL> select dbms_metadata.get_ddl('INDEX','CUST_IDX1') from dual;

DBMS_METADATA.GET_DDL('INDEX','CUST_IDX1')
--------------------------------------------------------------------------------

  CREATE INDEX "TESTIDX"."CUST_IDX1" ON "TESTIDX"."CUST" ("LAST_NAME")
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS N
OLOGGING
  STORAGE(INITIAL 131072 NEXT 131072 MINEXTENTS 1 MAXEXTENTS 2147483
645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_C
ACHE DEFAULT)
  TABLESPACE "REPORTING_INDEX"

*  顯示當前鏈接用戶全部索引的元數據,可運行如下代碼
set long 10000000
select dbms_metadata.get_ddl('INDEX',index_name) from user_indexes;

SQL> set long 10000000
SQL> select dbms_metadata.get_ddl('INDEX',index_name) from user_indexes;

DBMS_METADATA.GET_DDL('INDEX',INDEX_NAME)
--------------------------------------------------------------------------------

  CREATE INDEX "TESTIDX"."ADDR_FK1" ON "TESTIDX"."ADDRESS" ("CUST_ID")
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS N
OLOGGING
  STORAGE(INITIAL 131072 NEXT 131072 MINEXTENTS 1 MAXEXTENTS 2147483
645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_C
ACHE DEFAULT)
  TABLESPACE "REPORTING_INDEX"

DBMS_METADATA.GET_DDL('INDEX',INDEX_NAME)
--------------------------------------------------------------------------------

  CREATE INDEX "TESTIDX"."CUST_IDX1" ON "TESTIDX"."CUST" ("LAST_NAME")
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS N
OLOGGING
  STORAGE(INITIAL 131072 NEXT 131072 MINEXTENTS 1 MAXEXTENTS 2147483
645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_C
ACHE DEFAULT)
  TABLESPACE "REPORTING_INDEX"

DBMS_METADATA.GET_DDL('INDEX',INDEX_NAME)
--------------------------------------------------------------------------------

  CREATE UNIQUE INDEX "TESTIDX"."CUST_PK" ON "TESTIDX"."CUST" ("CUST_ID")
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTIC
S NOLOGGING
  STORAGE(INITIAL 131072 NEXT 131072 MINEXTENTS 1 MAXEXTENTS 2147
483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "REPORTING_INDEX"

DBMS_METADATA.GET_DDL('INDEX',INDEX_NAME)
--------------------------------------------------------------------------------
  CREATE UNIQUE INDEX "TESTIDX"."CUST_UK1" ON "TESTIDX"."CUST" ("LAST_NAME",
"FIRST_NAME")
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS NOLOGGI
NG
  STORAGE(INITIAL 131072 NEXT 131072 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE D
EFAULT)
  TABLESPACE "REPORTING_INDEX"

SQL>

刪除B樹索引

管理帶約束的B樹索引

輸入一個模式,查詢是否存在相應的外鍵約束

set linesize 300
col CHECKER for a20
col INDEX_TYPE for a20
col OWNER for a20
col TABLE_NAME for a20
col INDEX_NAME for a20
col CONSTRAINT_NAME for a20
col COLS for a20

SELECT
 CASE WHEN ind.index_name IS NOT NULL THEN
   CASE WHEN ind.index_type IN ('BITMAP') THEN
     '** Bitmp idx **'
   ELSE
     'indexed'
   END
 ELSE
   '** Check idx **'
 END checker
,ind.index_type
,cons.owner, cons.table_name, ind.index_name, cons.constraint_name, cons.cols
FROM (SELECT
        c.owner, c.table_name, c.constraint_name
       ,LISTAGG(cc.column_name, ',' ) WITHIN GROUP (ORDER BY cc.column_name) cols
      FROM dba_constraints  c
          ,dba_cons_columns cc
      WHERE c.owner           = cc.owner
      AND   c.owner = UPPER('&&schema')
      AND   c.constraint_name = cc.constraint_name
      AND   c.constraint_type = 'R'
      GROUP BY c.owner, c.table_name, c.constraint_name) cons
LEFT OUTER JOIN
(SELECT
  table_owner, table_name, index_name, index_type, cbr
 ,LISTAGG(column_name, ',' ) WITHIN GROUP (ORDER BY column_name) cols
 FROM (SELECT
        ic.table_owner, ic.table_name, ic.index_name
       ,ic.column_name, ic.column_position, i.index_type
       ,CONNECT_BY_ROOT(ic.column_name) cbr
       FROM dba_ind_columns ic
           ,dba_indexes     i
       WHERE ic.table_owner = UPPER('&&schema')
       AND   ic.table_owner = i.table_owner
       AND   ic.table_name  = i.table_name
       AND   ic.index_name  = i.index_name
       CONNECT BY PRIOR ic.column_position-1 = ic.column_position
       AND PRIOR ic.index_name = ic.index_name)
  GROUP BY table_owner, table_name, index_name, index_type, cbr) ind
ON  cons.cols       = ind.cols
AND cons.table_name = ind.table_name
AND cons.owner      = ind.table_owner
ORDER BY checker, cons.owner, cons.table_name;

set linesize 300
col CHECKER for a20
col INDEX_TYPE for a20
col OWNER for a20
col TABLE_NAME for a20
col INDEX_NAME for a20
col CONSTRAINT_NAME for a20
col COLS for a20

SQL> SELECT
  2   CASE WHEN ind.index_name IS NOT NULL THEN
  3     CASE WHEN ind.index_type IN ('BITMAP') THEN
  4       '** Bitmp idx **'
  5     ELSE
  6       'indexed'
  7     END
  8   ELSE
  9     '** Check idx **'
 10   END checker
 11  ,ind.index_type
 12  ,cons.owner, cons.table_name, ind.index_name, cons.constraint_name, cons.cols
 13  FROM (SELECT
 14          c.owner, c.table_name, c.constraint_name
 15         ,LISTAGG(cc.column_name, ',' ) WITHIN GROUP (ORDER BY cc.column_name) cols
 16        FROM dba_constraints  c
 17            ,dba_cons_columns cc
 18        WHERE c.owner           = cc.owner
 19        AND   c.owner = UPPER('&&schema')
 20        AND   c.constraint_name = cc.constraint_name
 21        AND   c.constraint_type = 'R'
 22        GROUP BY c.owner, c.table_name, c.constraint_name) cons
 23  LEFT OUTER JOIN
 24  (SELECT
 25    table_owner, table_name, index_name, index_type, cbr
 26   ,LISTAGG(column_name, ',' ) WITHIN GROUP (ORDER BY column_name) cols
 27   FROM (SELECT
 28          ic.table_owner, ic.table_name, ic.index_name
 29         ,ic.column_name, ic.column_position, i.index_type
 30         ,CONNECT_BY_ROOT(ic.column_name) cbr
 31         FROM dba_ind_columns ic
 32             ,dba_indexes     i
 33         WHERE ic.table_owner = UPPER('&&schema')
 34         AND   ic.table_owner = i.table_owner
 35         AND   ic.table_name  = i.table_name
 36         AND   ic.index_name  = i.index_name
 37         CONNECT BY PRIOR ic.column_position-1 = ic.column_position
 38         AND PRIOR ic.index_name = ic.index_name)
 39    GROUP BY table_owner, table_name, index_name, index_type, cbr) ind
 40  ON  cons.cols       = ind.cols
 41  AND cons.table_name = ind.table_name
 42  AND cons.owner      = ind.table_owner
 43  ORDER BY checker, cons.owner, cons.table_name;
Enter value for schema: testidx
old  19:       AND   c.owner = UPPER('&&schema')
new  19:       AND   c.owner = UPPER('testidx')
old  33:        WHERE ic.table_owner = UPPER('&&schema')
new  33:        WHERE ic.table_owner = UPPER('testidx')

CHECKER              INDEX_TYPE           OWNER                TABLE_NAME           INDEX_NAME           CONSTRAINT_NAME      COLS
-------------------- -------------------- -------------------- -------------------- -------------------- -------------------- --------------------
indexed              NORMAL               TESTIDX              ADDRESS              ADDR_FK1             ADDR_FK1             CUST_ID
相關文章
相關標籤/搜索