表外來鍵未加索引之處理
就像在Tom程式設計藝術中提到的,如果資料庫中確實存在死鎖,那麼需要檢視是否存在未加索引的外來鍵,而且在99%的情況下都會發現表中確實存在這個問題。只需對外來鍵加索引,死鎖(以及大量其他的競爭問題)都會煙消雲散。下面的例子就展示瞭如何使用這個指令碼來找出未加索引的外來鍵:
SQL> show user
USER is "SCOTT"
SQL> col columns for a20
SQL>
select table_name, constraint_name, cname1 || nvl2(cname2,','||cname2,null) || nvl2(cname3,','||cname3,null) || nvl2(cname4,','||cname4,null) ||
nvl2(cname5,','||cname5,null) || nvl2(cname6,','||cname6,null) || nvl2(cname7,','||cname7,null) || nvl2(cname8,','||cname8,null)
columns from ( select b.table_name, b.constraint_name,
max(decode( position, 1, column_name, null )) cname1,
max(decode( position, 2, column_name, null )) cname2,
max(decode( position, 3, column_name, null )) cname3,
max(decode( position, 4, column_name, null )) cname4,
max(decode( position, 5, column_name, null )) cname5,
max(decode( position, 6, column_name, null )) cname6,
max(decode( position, 7, column_name, null )) cname7,
max(decode( position, 8, column_name, null )) cname8,
count(*) col_cnt
from (select substr(table_name,1,30) table_name,
substr(constraint_name,1,30) constraint_name,
substr(column_name,1,30) column_name,
position
from user_cons_columns ) a,
user_constraints b
where a.constraint_name = b.constraint_name
and b.constraint_type = 'R'
group by b.table_name, b.constraint_name
) cons
where col_cnt > ALL ( select count(*) from user_ind_columns i where i.table_name = cons.table_name and i.column_name in (cname1, cname2, cname3, cname4,ame4,
cname5, cname6, cname7, cname8 )
and i.column_position <= cons.col_cnt group by i.index_name
26 )
SQL> /
TABLE_NAME CONSTRAINT_NAME COLUMNS
------------------------------ ------------------------------ --------------------
C SYS_C006026 X
EMP FK_DEPTNO DEPTNO
SCHEME_LABLE_RERATION_TABLE FK_SCHEME_L_REFERENCE_LABLEINF LABLE_ID
這個指令碼將處理外來鍵約束,其中最多可以有8列(如果你的外來鍵有更多的列,可能就得重新考慮一下你的設計了)。
首先,它在前面的查詢中建立一個名為CONS的內聯檢視(inline view)。這個內聯檢視將約束中適當的列名從行轉置到列,其結果是每個約束有一行,最多有8 列,這些列分別取值為約束中的列名。另外,這個檢視中還有一個列COL_CNT,
其中包含外來鍵約束本身的列數。對於這個內聯檢視中返回的每一行,我們要執行一個關聯子查詢(correlated subquery),檢查當前所處理表上的所有索引。它會統計出索引中與外來鍵約束中的列相匹配的列數,然後按索引名分組。這樣,就能生成一組數,每個數都是該表某個索引中匹配列的總計。如果原來的COL_CNT大於所有這些數,那麼表中就沒有支援這個約束的索引。如果COL_CNT小於所有這些數,就至少有一個索引支援這個約束。注意,這裡使用了NVL2 函式,我們用這個函式把列名列表“粘到”一個用逗號分隔的列表中。這個函式有3 個引數:A、B 和C。如果引數A非空,則返回B;否則返回引數C。這個查詢有一個前提,假設約束的所有者也是表和索引的所有者。如果另一位使用者對錶加索引,或者表在另一個模式中(這兩種情況都很少見),就不能正確地工作。所以,這個指令碼展示出,表C在列X上有一個外來鍵,但是沒有索引。透過對X加索引,就可以完全消除這個鎖定問題。
除了全表鎖外,在以下情況下,未加索引的外來鍵也可能帶來問題:
--如果有ON DELETE CASCADE,而且沒有對子表加索引:例如,EMP是DEPT的子表,DELETE DEPTNO = 10應該CASCADE(級聯)至EMP[4]。如果EMP中的DEPTNO沒有索引,那麼刪除DEPT 表中的每一行時都會對EMP 做一個全表掃描。這個全表掃描可能是不必要的,而且如果從父表刪除多行,父表中每刪除一行就要掃描一次子表。
--從父表查詢子表:再次考慮EMP/DEPT 例子。利用DEPTNO 查詢EMP 表是相當常見的。如果頻繁地執行以下查詢(例如,生成一個報告),你會發現沒有索引會使查詢速度變慢:
§ select * from dept, emp
§ where emp.deptno = dept.deptno and dept.deptno = :X;
那麼,什麼時候不需要對外來鍵加索引呢?
答案是,一般來說,當滿足以下條件時不需要加索引:
--沒有從父表刪除行。
--沒有更新父表的惟一鍵/主鍵值(當心工具有時會無意地更新主鍵!)。
--沒有從父表聯結子表(如DEPT聯結到EMP)。
如果滿足上述全部3 個條件,那你完全可以跳過索引,不需要對外來鍵加索引。如果滿足以上的某個條件,就要當心加索引的後果。這是一種少有的情況,即Oracle“過分地鎖定了”資料。
然後,可用一下sql生成建立index的語句:
SQL>
select 'create index IX_'||table_name||'_'|| cname1 || nvl2(cname2,'_'||cname2,null) || nvl2(cname3,'_'||cname3,null) || nvl2(cname4,'_'||cname4,null) ||l) ||
nvl2(cname5,'_'||cname5,null) || nvl2(cname6,'_'||cname6,null) || nvl2(cname7,'_'||cname7,null) || nvl2(cname8,'_'||cname8,null)||' ON '||table_name|| '('|| cname1 || nvl2(cname2,','||cname2,null) || nvl2(cname3,','||cname3,null) || nvl2(cname4,','||cname4,null) || nvl2(cname5,','||cname5,null) || nvl2(cname6,','||cname6,null) || nvl2(cname7,','||cname7,null) || nvl2(cname8,','||cname8,null) || ');' ');'
columns from ( select b.table_name, b.constraint_name,
max(decode( position, 1, column_name, null )) cname1,
max(decode( position, 2, column_name, null )) cname2,
max(decode( position, 3, column_name, null )) cname3,
max(decode( position, 4, column_name, null )) cname4,
max(decode( position, 5, column_name, null )) cname5,
max(decode( position, 6, column_name, null )) cname6,
max(decode( position, 7, column_name, null )) cname7,
max(decode( position, 8, column_name, null )) cname8,
count(*) col_cnt
from (select substr(table_name,1,30) table_name, substr(constraint_name,1,30) constraint_name, substr(column_name,1,30) column_name, position from user_cons_columns ) a, user_constraints b where a.constraint_name = b.constraint_name and b.constraint_type = 'R' group by b.table_name, b.constraint_name ) cons cons
where col_cnt > ALL ( select count(*) from user_ind_columns i where i.table_name = cons.table_name and i.column_name in (cname1, cname2, cname3, cname4,ame4,
cname5, cname6, cname7, cname8 ) and i.column_position <= cons.col_cnt group by i.index_name
16 )
17 /
SQL> show user
USER is "SCOTT"
SQL> col columns for a20
SQL>
select table_name, constraint_name, cname1 || nvl2(cname2,','||cname2,null) || nvl2(cname3,','||cname3,null) || nvl2(cname4,','||cname4,null) ||
nvl2(cname5,','||cname5,null) || nvl2(cname6,','||cname6,null) || nvl2(cname7,','||cname7,null) || nvl2(cname8,','||cname8,null)
columns from ( select b.table_name, b.constraint_name,
max(decode( position, 1, column_name, null )) cname1,
max(decode( position, 2, column_name, null )) cname2,
max(decode( position, 3, column_name, null )) cname3,
max(decode( position, 4, column_name, null )) cname4,
max(decode( position, 5, column_name, null )) cname5,
max(decode( position, 6, column_name, null )) cname6,
max(decode( position, 7, column_name, null )) cname7,
max(decode( position, 8, column_name, null )) cname8,
count(*) col_cnt
from (select substr(table_name,1,30) table_name,
substr(constraint_name,1,30) constraint_name,
substr(column_name,1,30) column_name,
position
from user_cons_columns ) a,
user_constraints b
where a.constraint_name = b.constraint_name
and b.constraint_type = 'R'
group by b.table_name, b.constraint_name
) cons
where col_cnt > ALL ( select count(*) from user_ind_columns i where i.table_name = cons.table_name and i.column_name in (cname1, cname2, cname3, cname4,ame4,
cname5, cname6, cname7, cname8 )
and i.column_position <= cons.col_cnt group by i.index_name
26 )
SQL> /
TABLE_NAME CONSTRAINT_NAME COLUMNS
------------------------------ ------------------------------ --------------------
C SYS_C006026 X
EMP FK_DEPTNO DEPTNO
SCHEME_LABLE_RERATION_TABLE FK_SCHEME_L_REFERENCE_LABLEINF LABLE_ID
這個指令碼將處理外來鍵約束,其中最多可以有8列(如果你的外來鍵有更多的列,可能就得重新考慮一下你的設計了)。
首先,它在前面的查詢中建立一個名為CONS的內聯檢視(inline view)。這個內聯檢視將約束中適當的列名從行轉置到列,其結果是每個約束有一行,最多有8 列,這些列分別取值為約束中的列名。另外,這個檢視中還有一個列COL_CNT,
其中包含外來鍵約束本身的列數。對於這個內聯檢視中返回的每一行,我們要執行一個關聯子查詢(correlated subquery),檢查當前所處理表上的所有索引。它會統計出索引中與外來鍵約束中的列相匹配的列數,然後按索引名分組。這樣,就能生成一組數,每個數都是該表某個索引中匹配列的總計。如果原來的COL_CNT大於所有這些數,那麼表中就沒有支援這個約束的索引。如果COL_CNT小於所有這些數,就至少有一個索引支援這個約束。注意,這裡使用了NVL2 函式,我們用這個函式把列名列表“粘到”一個用逗號分隔的列表中。這個函式有3 個引數:A、B 和C。如果引數A非空,則返回B;否則返回引數C。這個查詢有一個前提,假設約束的所有者也是表和索引的所有者。如果另一位使用者對錶加索引,或者表在另一個模式中(這兩種情況都很少見),就不能正確地工作。所以,這個指令碼展示出,表C在列X上有一個外來鍵,但是沒有索引。透過對X加索引,就可以完全消除這個鎖定問題。
除了全表鎖外,在以下情況下,未加索引的外來鍵也可能帶來問題:
--如果有ON DELETE CASCADE,而且沒有對子表加索引:例如,EMP是DEPT的子表,DELETE DEPTNO = 10應該CASCADE(級聯)至EMP[4]。如果EMP中的DEPTNO沒有索引,那麼刪除DEPT 表中的每一行時都會對EMP 做一個全表掃描。這個全表掃描可能是不必要的,而且如果從父表刪除多行,父表中每刪除一行就要掃描一次子表。
--從父表查詢子表:再次考慮EMP/DEPT 例子。利用DEPTNO 查詢EMP 表是相當常見的。如果頻繁地執行以下查詢(例如,生成一個報告),你會發現沒有索引會使查詢速度變慢:
§ select * from dept, emp
§ where emp.deptno = dept.deptno and dept.deptno = :X;
那麼,什麼時候不需要對外來鍵加索引呢?
答案是,一般來說,當滿足以下條件時不需要加索引:
--沒有從父表刪除行。
--沒有更新父表的惟一鍵/主鍵值(當心工具有時會無意地更新主鍵!)。
--沒有從父表聯結子表(如DEPT聯結到EMP)。
如果滿足上述全部3 個條件,那你完全可以跳過索引,不需要對外來鍵加索引。如果滿足以上的某個條件,就要當心加索引的後果。這是一種少有的情況,即Oracle“過分地鎖定了”資料。
然後,可用一下sql生成建立index的語句:
SQL>
select 'create index IX_'||table_name||'_'|| cname1 || nvl2(cname2,'_'||cname2,null) || nvl2(cname3,'_'||cname3,null) || nvl2(cname4,'_'||cname4,null) ||l) ||
nvl2(cname5,'_'||cname5,null) || nvl2(cname6,'_'||cname6,null) || nvl2(cname7,'_'||cname7,null) || nvl2(cname8,'_'||cname8,null)||' ON '||table_name|| '('|| cname1 || nvl2(cname2,','||cname2,null) || nvl2(cname3,','||cname3,null) || nvl2(cname4,','||cname4,null) || nvl2(cname5,','||cname5,null) || nvl2(cname6,','||cname6,null) || nvl2(cname7,','||cname7,null) || nvl2(cname8,','||cname8,null) || ');' ');'
columns from ( select b.table_name, b.constraint_name,
max(decode( position, 1, column_name, null )) cname1,
max(decode( position, 2, column_name, null )) cname2,
max(decode( position, 3, column_name, null )) cname3,
max(decode( position, 4, column_name, null )) cname4,
max(decode( position, 5, column_name, null )) cname5,
max(decode( position, 6, column_name, null )) cname6,
max(decode( position, 7, column_name, null )) cname7,
max(decode( position, 8, column_name, null )) cname8,
count(*) col_cnt
from (select substr(table_name,1,30) table_name, substr(constraint_name,1,30) constraint_name, substr(column_name,1,30) column_name, position from user_cons_columns ) a, user_constraints b where a.constraint_name = b.constraint_name and b.constraint_type = 'R' group by b.table_name, b.constraint_name ) cons cons
where col_cnt > ALL ( select count(*) from user_ind_columns i where i.table_name = cons.table_name and i.column_name in (cname1, cname2, cname3, cname4,ame4,
cname5, cname6, cname7, cname8 ) and i.column_position <= cons.col_cnt group by i.index_name
16 )
17 /
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/628922/viewspace-733184/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Oracle 外來鍵的級聯處理Oracle
- oracle查詢表資訊(索引,外來鍵,列等)Oracle索引
- 10.30 索引,外來鍵索引
- 如果新增欄位是外來鍵,如何處理?
- Oracle外來鍵約束中NULL的處理OracleNull
- oracle外來鍵索引解決父表鎖定問題Oracle索引
- truncate有外來鍵約束的表,報ORA-02266處理。
- 外來鍵缺索引檢查指令碼索引指令碼
- 查詢沒有索引的外來鍵索引
- 查詢(看)表的主鍵、外來鍵、唯一性約束和索引索引
- 遷移後處理外來鍵約束的問題
- ORACLE: 查詢(看)表的主鍵、外來鍵、唯一性約束和索引Oracle索引
- Oracle 外來鍵索引影響阻塞問題Oracle索引
- 在已存在的表結構上新增主鍵、外來鍵、聯合主鍵、聯合索引的例子索引
- 檢查外來鍵是否有索引的指令碼索引指令碼
- 父表修改與外來鍵的關係(主鍵DML與外來鍵的關係)
- Django(15)外來鍵和表關係Django
- 查詢一個表的外來鍵
- 外來鍵沒有索引哪些DML操作會被阻塞索引
- 【fk_index】外來鍵中有無索引的區別Index索引
- SQL SERVER中找出拙劣的約束,索引,外來鍵SQLServer索引
- oracle全文索引之幾個關鍵表Oracle索引
- 10個用來處理鍵盤事件的JQuery外掛和JS類庫事件jQueryJS
- 通過外來鍵連線多個表
- 索引表空間不足的幾個處理思路索引
- 外來鍵有無索引帶來的影響學習與測試索引
- sqlserver外來鍵SQLServer
- MySQL 用隨機資料填充外來鍵表MySql隨機
- Oracle查詢表的外來鍵引用關係Oracle
- 兩表互為外來鍵的解決方案
- [資料庫][分庫分表]分庫分表之後,id主鍵如何處理資料庫
- 反向索引處理前%索引
- 批量修改欄位長度,考慮主鍵外來鍵索引的情況【轉】索引
- oracle之 反向鍵索引Oracle索引
- indexedDB 內鍵與外來鍵Index
- 通過在Oracle子表外來鍵上建立索引提高效能Oracle索引
- MYSQL的外來鍵MySql
- 外來鍵技術