如果你有兩個數據來源如平面文件或表數據並且要將他們合並在一起你將怎麼做?如果他們有一個共同的屬性如客戶ID那麼該解決方案應該是很明顯合並相關的屬性在這個例子中只需合並客戶ID就夠了如果沒有任何共同之處該怎麼辦呢?唯一的要求就是將數據源中的記錄和數據源中的記錄進行匹配 並且那個記錄去和另一個記錄匹配並沒有關系那麼問題是一個數據源中的每一個紀錄如何獲得從其他數據源記錄的標記
上述問題可以被描述為向一個數據庫中加入了不同的或看似無關的數據在先前的文章的文章中涉及如何使用ROWNUM在無關的數據之間創造聯系該合並方法的本質是利用甲骨文提供虛擬數據列來建立聯系下面的查詢可以用來作為CREATE TABLE AS SELECT聲明的一部分或作為基於滿足加入條件既定目標表的插入
SELECT * FROM
(SELECT ROWNUM AS rownum_a
FROM TABLE_A
) ALIAS_A
(SELECT ROWNUM AS rownum_b
FROM TABLE_B
) ALIAS_B
WHERE ALIAS_Arownum_a = ALIAS_Brownum_b;
假設要合並的記錄的數目過大(如數以百萬計)這種方法潛在的缺點是什麼?那麼當一行作為一個記錄時又如何了?我們沒有真正的控制權決定的查詢所返回結果行的順序直到我們執行查詢之前甲骨文是不知道記錄的行號的換言之 ROWNUM是在這樣的事實上創建的如果你要從兩個地方選擇數百萬行你將支付甲骨文公司為每個記錄分配行號(只針對你的查詢而不是永遠)的時間
讓我們監測將兩個有萬行的表合並到一起的一個會話在這第一個例子中這個數據源已經記錄可萬個記錄表A范圍從到及表B范圍從至 (即在第一個表中再加入萬行) 如果加入後能夠完美的保持行的順序那麼有序對將像下面表格這個樣子
當我們查看數據時(通過Toad)發現Oracle數據庫並不執行一個完美的排序並且相差甚遠
該ROWNUM_A和B值一個一個都匹配因為這是我們匹配/合並的注意記錄 (和 )是如何同ROWNUM 標記在一起的 所以我們可以推斷是甲骨文以同樣的方式填補表格之間的空白區塊這應該說服你一次甚至永遠(如果你至今還不知知道) ROWNUM虛擬數據列已沒有意義或與個表中記錄的實際順序無關
創建表的聲明追蹤 經過TKPROF 解析後輸出結果如下
CREATE TABLE TABLE_ROWNUM AS
SELECT * FROM
(SELECT ID ROWNUM AS rownum_a
FROM TABLE_A)
ALIAS_A
(SELECT ID AS id_b ROWNUM AS rownum_b
FROM TABLE_B)
ALIAS_B
WHERE ALIAS_Arownum_a = ALIAS_Brownum_b
call count cpu elapsed disk query current rows
Parse
Execute
Fetch
total
我們知道一個事實即每個表都有萬行在分析了表後 NUM_ROWS值顯示為 當與甲骨文本身將通過連續計數報告的值相比較時要小心依靠通過第三方工具檢查出的值(包括從USER_TABLES選擇NUM_ROWS )為什麼會有差異呢?是否是因為分析是基於樣本或估算的數據或根據檢查到的每個記錄?
現在對於合並數據有一個可供選擇的辦法那就是讓我們使用一個真正的列替代虛擬數據列一個自然的選擇是創建(在某種意義上)基於序列替代關鍵字這個辦法是為每個表添加一個命名為SEQ的列並且在基於序列號對他們進行更新並且保證每次更新使用相同的起點和相同的增量對一個表更新操作如下所示
SQL> create sequence tab_b;
Sequence created
Elapsed: ::
SQL> update table_b set seq = tab_bnextval;
rows updated
Elapsed: ::
有一件事應該可以立即脫穎而出創造一個合並關鍵字所花費的時間剛剛超過五分鐘或是ROWNUM采取的方法所花費時間的倍這只是對兩個表中的一個表所進行操作所花費的時間(第一張表格花費五分鐘進行更新) 增加或創建一個合並關鍵字是必要的如有可能最好在創建表的時候就創建那麼比通過ROWNUM做同樣的事情所多花費的關鍵點是什麼?
根據新的設置如何進行合並?
CREATE TABLE TABLE_SEQ AS
SELECT * FROM
(SELECT ID SEQ AS seq_a
FROM TABLE_A)
ALIAS_A
(SELECT ID AS id_b SEQ AS seq_b
FROM TABLE_B)
ALIAS_B
WHERE ALIAS_Aseq_a = ALIAS_Bseq_b
call count cpu elapsed disk query current rows
Parse
Execute
Fetch
total
有趣的是既然數據並非如此不同性能也只是略差那麼解釋計劃展示的是什麼?使用ROWNUM原始測試我們有
PLAN_TABLE_OUTPUT
Plan hash value:
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
| | CREATE TABLE STATEMENT | | G| G| | M ()| :: |
| | LOAD AS SELECT | TABLE_ROWNUM | | | | | |
|* | HASH JOIN | | G| G| M| K ()| :: |
| | VIEW | | K| M| | ()| :: |
| | COUNT | | | | | | |
| | TABLE ACCESS FULL | TABLE_B | K| K| | ()| :: |
| | VIEW | | K| M| | ()| :: |
| | COUNT | | | | | | |
| | TABLE ACCESS FULL | TABLE_A | K| K| | ()| :: |
Predicate Information (identified by operation id):
access(ALIAS_AROWNUM_A=ALIAS_BROWNUM_B)
基於序列的合並似乎是一個更好的計劃
PLAN_TABLE_OUTPUT
Plan hash value:
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
| | CREATE TABLE STATEMENT | | G| G| | M ()| :: |
| | LOAD AS SELECT | TABLE_ROWNUM | | | | | |
|* | HASH JOIN | | G| G| M| K ()| :: |
| | VIEW | | K| M| | ()| :: |
| | COUNT | | | | | | |
| | TABLE ACCESS FULL | TABLE_B | K| K| | ()| :: |
| | VIEW | | K| M| | ()| :: |
| | COUNT | | | | | | |
| | TABLE ACCESS FULL | TABLE_A | K| K| | ()| :: |
Predicate Information (identified by operation id):
access(ALIAS_AROWNUM_A=ALIAS_BROWNUM_B)
雖然這是一個相對較小的數據集你可以明白為什麼執行該計劃的花費可能會引起誤解如果基於序列的表在同一會話中被刪除和重新建立創建表重新刪除的時間到剛剛超過秒在表面上看第二輪創建的表似乎要快得多但真正要證明的是什麼呢?
所要證明的是數據塊已經讀入緩存從緩存中讀取數據塊的速度將遠遠超過從磁盤雙方讀取的速度(這我們已經知道的事實) 它實際意義是你創建表需要多少時間?這通常是一次性完成如果原始表被刪除和重創它的創建時間將大大加快
通過清除共享池和緩存來恢復性能 在ROWNUM和基於序的列情況下所花費的時間分別 秒和秒的在這一點上它可能看起來像是混為一談但在運行期間其性能級別交換了這也許是事實但不要忘記設置了序列為基礎的表格的費用(按時間)
總結
從某種意義上說最為相似的數據集操作系統和平台依賴性(多少行內存和I / O等) 他們可以更快地在不同數據集之間添加一個共同的屬性然後在進行合並操作對於較小的數據集也許略高於萬行我冒昧地說使用ROWNUM這將永遠是比新增一個合並關鍵字更快即使使用常見的關鍵創建表的速度更快那麼什麼時候適當使用ROWNUM ?當在沒有共同關鍵字的情況時你不關心表之間的特殊關聯即使是正好就存在這樣的事實如果你正在處理相關表他們基於一個共同的屬性並且這些關聯必須排序你一定不能依賴ROWNUM保持合並表之間的順序它事關在一個表中具體行是否與第二個表中特定行匹配
From:http://tw.wingwit.com/Article/program/Oracle/201311/18983.html