ORACLE--預編譯與共享池--SGA基本介紹
我們暫時先不說其他的,我們先做一個簡單的實驗來證明來看出一些問題,最後通過為什麼來說明實驗的結論,並介紹原理和常規查看方式,好了,我們先看看下麵三段代碼分別執行的結果。
首先為了測試,我們需要創建一張表:
CREATE TABLE PRE_TEST_TABLE(
C1 NUMBER,
C2 VARCHAR2(100)
);
好了,我們做一個插入操作的對比:
代碼段1:
BEGIN
FOR I IN 1..20000 LOOP
EXECUTE IMMEDIATE 'INSERT INTO PRE_TEST_TABLE VALUES('||i||',''test'')';
END LOOP;
COMMIT;
END;
代碼片段2:
BEGIN
FOR I IN 1..20000 LOOP
EXECUTE IMMEDIATE 'INSERT INTO PRE_TEST_TABLE VALUES(:V1,:V2)' USING I,'test';
END LOOP;
COMMIT;
END;
代碼片段3:
BEGIN
FOR I IN 1..20000 LOOP
INSERT INTO PRE_TEST_TABLE VALUES(I,'TEST');
END LOOP;
COMMIT;
END;
三段代碼執行效率你可以清晰的對比出來,代碼段1是最慢的,而且比後兩者慢很多倍,而代碼片段2和代碼片段3執行效率基本是一樣的,為什麼會有這樣的效果呢?看了下麵的推敲,我們就清楚了,我們先把數據清理掉,共享池清理一下(下麵我們再說),在做操作比較好。
現在我需要做的SQL語句操作是對表的插入PRE_TEST_TABLE VALUES表的操作:
INSERT INTO PRE_TEST_TABLE VALUES....
至於參數如何,我們不一定,首先查詢一下共享池內部做此操作的記錄:
SELECT * FROM V$SQLAREA
WHERE SQL_TEXT LIKE 'INSERT INTO PRE_TEST_TABLE VALUES%';
發現數據太多,有多少不好說了,暫時不數了,因為對得出結論沒有意義,需要清理下共享池方便試驗。
TRUNCATE TABLE PRE_TEST_TABLE;//清空表
ALTER SYSTEM FLUSH SHARED_POOL;//清空緩衝區
查詢共享池:
SELECT * FROM V$SQLAREA
WHERE SQL_TEXT LIKE 'INSERT INTO PRE_TEST_TABLE VALUES%';
發現沒有任何數據。。。。我們開始比較幹淨的做實驗了。。。
開始試驗:
我們將上述試驗的循環次數降低為3次
首先執行代碼段1:
BEGIN
FOR I IN 1..3 LOOP
EXECUTE IMMEDIATE 'INSERT INTO PRE_TEST_TABLE VALUES('||i||',''test'')';
END LOOP;
COMMIT;
END;
查詢共享池(發現多了3條記錄):
SQL> SELECT SQL_TEXT,HASH_VALUE,PARSE_CALLS FROM V$SQLAREA
2 WHERE SQL_TEXT LIKE 'INSERT INTO PRE_TEST_TABLE VALUES%';
SQL_TEXT HASH_VALUE PARSE_CALLS
--------------------------------------------------------------- -------------- ---------------------
INSERT INTO PRE_TEST_TABLE VALUES(1,'test') 2868385396 1
INSERT INTO PRE_TEST_TABLE VALUES(2,'test') 2513099158 1
INSERT INTO PRE_TEST_TABLE VALUES(3,'test') 455953479 1
再執行第二段代碼:
BEGIN
FOR I IN 1..3 LOOP
EXECUTE IMMEDIATE 'INSERT INTO PRE_TEST_TABLE VALUES(:V1,:V2)' USING I,'test';
END LOOP;
COMMIT;
END;
再查詢共享池(發現隻多了一條SQL):
SQL> SELECT SQL_TEXT,HASH_VALUE,PARSE_CALLS FROM V$SQLAREA
2 WHERE SQL_TEXT LIKE 'INSERT INTO PRE_TEST_TABLE VALUES%';
SQL_TEXT HASH_VALUE PARSE_CALLS
--------------------------------------------------------------- ---------------- ------------
INSERT INTO PRE_TEST_TABLE VALUES(1,'test') 2868385396 1
INSERT INTO PRE_TEST_TABLE VALUES(:V1,:V2) 357326048 3
INSERT INTO PRE_TEST_TABLE VALUES(2,'test') 2513099158 1
INSERT INTO PRE_TEST_TABLE VALUES(3,'test') 455953479 1
再執行第三段代碼:
BEGIN
FOR I IN 1..3 LOOP
INSERT INTO PRE_TEST_TABLE VALUES(I,'TEST');
END LOOP;
COMMIT;
END;
再查詢共享池(發現也隻多了一條SQL):
SQL> SELECT SQL_TEXT,HASH_VALUE,PARSE_CALLS FROM V$SQLAREA
2 WHERE SQL_TEXT LIKE 'INSERT INTO PRE_TEST_TABLE VALUES%';
SQL_TEXT HASH_VALUE PARSE_CALLS
------------------------------------------------------------- ------------------ ------------
INSERT INTO PRE_TEST_TABLE VALUES(1,'test') 2868385396 1
INSERT INTO PRE_TEST_TABLE VALUES(:B1 ,'TEST') 2239119514 3
INSERT INTO PRE_TEST_TABLE VALUES(:V1,:V2) 357326048 3
INSERT INTO PRE_TEST_TABLE VALUES(2,'test') 2513099158 1
INSERT INTO PRE_TEST_TABLE VALUES(3,'test') 455953479 1
從這裏可能大家基本可以得出初步的結論,就是第一段代碼每條SQL都會占用共享池,並征用共享池,而且用過一次後就不會在使用了,這個答案是正確的,我首先給出預編譯和拚SQL對ORACLE的四個不同影響進行定義,下麵再說明為什麼:
1、拚SQL會導致共享池的長期征用狀態
2、拚SQL會導致共享池中存在一堆的垃圾SQL。
3、拚SQL會硬解析(Hard parse),而預編譯可以充分利用軟解析。
4、拚SQL會導致注入型錯誤。
疑問:
為什麼第二段代碼和第三段代碼是一個效果呢,這裏說明一下,在存儲過程提高效率的基本因素就是你如果按照過程化去寫SQL,存儲過程默認就會按照預編譯方式去執行,因為這是ORACLE優化的基本原則,而即使在過程中使用EXECUTE IMMEDIATE SQL_STR去執行,請注意,EXECUTE IMMEDIATE不是SQL語句,而是ORACLE在過程中支持的命令,即直接發送執行命令給ORACLE的分析器,所以這個是否進行編譯完全看你的SQL是什麼樣的了,而不是ORACLE自己能決定的。
過程說明:
SQL從終端通過1521 TCP服務端口以字符串方式傳送至ORACLE後(包含JAVA程序也是這樣,其他應用程序,如出現String sql = "SELECT * FROM A WHERE ID="+id;也會出現和代碼段1一樣的結果),ORACLE通過HASH算法對其SQL轉換,並在共享池中查找是否存在同樣HASH值的SQL(即:SQL即使是參數或者大小寫不同,也會導致找不到一樣的HASH值),如果找到了,直接執行已經編譯完的SQL,並修改使用率(這個有用);若沒有,則首先通過硬解析工具對其進行各項語法分析和性能指標分析等等,然後開始征用共享池(此為共享資源),並注冊到共享池中,標誌調用次數為1,然後再執行,當在大量征用共享資源時候,並且在硬解析過程中,高並發將導致阻塞。可以將上述第一段代碼的循環次數增加為10萬,基本機器可以弄死,嗬嗬。。。。
共享池的大小肯定是有限製的,所以ORACLE在共享池不夠用的時候,采用基於LRU為核心的算法進行替換(上述的PARSE_CALLS字段可以基本看出SQL被調用的次數,但是不完全依賴於它),我們最希望的就是不要做這樣的操作,因為這樣的操作必然麵臨磁盤讀取,在內存中獲取我們稱為命中,命中率高才能提高利用率,係統的整體性能才能得到保證。
第四點中提到的至於注入型錯誤或攻擊就是傳送特殊字符串,導致SQL執行SQL的修改,為什麼,因為你的SQL是拚出來的,我舉個簡單例子:
你的程序中:
String sql = "SELECT * FROM A WHERE A.NAME = '"+name+"'";
此name假如為查詢條件傳入,那麼此時我在查詢條件文本框中這樣輸入:
第一種輸入:
' OR 1=1--
此時你的SQL變成:
SELECT * FROM A WHERE A.NAME ='' OR 1=1 --'
後麵兩杠是屏蔽你後麵的SQL,用一個OR 1=1前麵的東東不論是啥就永遠成立了(注意:ORACLE的SQL執行,優先級是AND 大於 OR的,所以隻要OR 1=1,無論有多少個AND,最後會和這個OR 1=1去匹配,所有的數據都會提取出來),此時分頁或者說導出控製天數或者數據量,都控製不了,有多少數據就會出來多少數據,首先數據權限沒有了,然後開幾個瀏覽器係統就能掛掉,嗬嗬!
另一種更加損的輸入招數是:
';DROP TABLE DUAL--
在我以前用SQL SERVER的JDBC時,這種方式是支持的,這樣是很損的一種辦法。其實輸入的方式有些通過URL有些通過條件,不一定,而且千奇百怪,有些想都想不到,至少他可能會導致你的SQL執行不是那麼順利篡改了SQL執行的原有意義。
所以OLTP係統使用這樣的SQL,尤其對於ORACLE數據庫(其餘數據庫請自己研究下),是非常惡心的,所以我們在OLTP係統是封殺這樣的SQL存在。
簡要共享池的使用介紹:
談到共享池我們大致介紹一些SGA的內容,ORACLE數據庫我們操作主要對象是實例,而非數據庫本身,主要原因為:性能、安全性。而實例大致分:SGA、PGA,本文隻是由共享池介紹一下SGA,細節說下其共享池部分,而PGA後續討論。
SGA內部主要包含:數據緩衝區、共享池、JAVA池、大池、Stream池、重做日誌緩衝區;
PGA內部包含:用戶Session信息、排序信息、Hash area、堆棧。這些信息被後台進程所控製,版本遞增的後台進程也在不斷增加,細節的信息後續討論。
其實今天所謂查詢共享池,也是查詢共享池內部的Library cache。我們在SQLPLUS中最常用的命令就是:
SQL> show sga;
Total System Global Area 1.7062E+10 bytes
Fixed Size 2102776 bytes
Variable Size 4613736968 bytes
Database Buffers 1.2432E+10 bytes
Redo Buffers 14671872 bytes
也可以使用一下方式查詢和上述一樣的SGA信息:
SQL> SELECT * FROM V$SGA;
NAME VALUE
-------------------- ----------
Fixed Size 2102776
Variable Size 4613736968
Database Buffers 1.2432E+10
Redo Buffers 14671872
分別解釋下幾個字段的意義:
Total System Global Area:代表SGA的總體大小,包含下麵幾者之和,都是以byte為單位,即字節;
Fixed Size:字典信息、控製信息、狀態信息。
Variable Size:共享池(shared pool)、Java池(Java Pool)、大池(Large Pool)、Stream pool;
Database Buffers:為數據緩衝區,OLTP係統要求這塊設置較大。
Redo Buffer:重做日誌緩衝區,適當提高緩衝區,減少文件組切換,可以提高效率。
通過一下SQL可以得到SGA內部詳細的組件分配情況:
SQL> SELECT * FROM V$SGASTAT;
POOL NAME BYTES
------------ -------------------------- ----------
fixed_sga 2102776
buffer_cache 1.2432E+10
log_buffer 14671872
shared pool transaction 8062512
shared pool table definiti 80336
shared pool KGSKI scheduler heap 2 de 232
shared pool kspd run-time context 16
shared pool PX subheap 61344
shared pool partitioning d 455480
shared pool message pool freequeue 757568
shared pool qesblGF:bfm 728
POOL NAME BYTES
------------ -------------------------- ----------
等等數據。。。。。。。自己查看一下即可,我這由於篇幅所限,就輸出這麼多了。
--下麵SQL用於查看SGA中可進行手工調配參數的列表:
SQL> SELECT * FROM V$SGA_DYNAMIC_COMPONENTS;
COMPONENT CURRENT_SIZE MIN_SIZE MAX_SIZE USER_SPECIFIED_SIZE OPER_COUNT LAST_OPER_TYP LAST_
---------------------------------------------------------------- ------------ ---------- ----------
shared pool 4194304000 4194304000 0 4194304000 0 STATIC
large pool 134217728 134217728 0 134217728 0 STATIC
java pool 134217728 134217728 0 134217728 0 STATIC
streams pool 117440512 117440512 0 117440512 0 STATIC
DEFAULT buffer cache 1.2264E+10 1.2264E+10 0 1.2264E+10 2 SHRINK MANUAL 07-4月
KEEP buffer cache 100663296 0 0 100663296 8 GROW MANUAL 07-4月 -10
RECYCLE buffer cache 67108864 0 0 67108864 1 GROW MANUAL 07-4月 -10
DEFAULT 2K buffer cache 0 0 0 0 0 STATIC
DEFAULT 4K buffer cache 0 0 0 0 0 STATIC
DEFAULT 8K buffer cache 0 0 0 0 0 STATIC
DEFAULT 16K buffer cache 0 0 0 0 0 STATIC
COMPONENT CURRENT_SIZE MIN_SIZE MAX_SIZE USER_SPECIFIED_SIZE OPER_COUNT LAST_OPER_TYP LAST_
---------------------------------------------------------------- ------------ ---------- ----------
DEFAULT 32K buffer cache 0 0 0 0 0 STATIC
ASM Buffer Cache 0 0 0 1.2465E+10 0 STATIC
查詢共享池大小:
SQL> show parameter shared_pool_size;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
shared_pool_size big integer 4000M
SQL> SELECT NAME,TYPE,VALUE
2 FROM V$PARAMETER A
3 WHERE A.NAME='shared_pool_size';
NAME TYPE VALUE
-------------------- ----------------------------- ----------------------------
shared_pool_size 6 4194304000
這裏順便說下,很多時候大家不知道數據字典是什麼,很多時候數據字典的名字很長,而且有些後麵又s,有些後麵沒有S,大家記錄不下來,此時大家隻需要知道大致是什麼就OK了,然後用下麵的基於視圖的總視圖去查詢視圖的實際名稱(我們以不知道共享池的視圖是什麼):
SQL> SELECT * FROM DICT T
2 WHERE T.TABLE_NAME LIKE '%V$SHARED%';
TABLE_NAME COMMENTS
------------------------------ -------------------------------------------------------------------
V$SHARED_SERVER_MONITOR Synonym for V_$SHARED_SERVER_MONITOR
V$SHARED_SERVER Synonym for V_$SHARED_SERVER
V$SHARED_POOL_RESERVED Synonym for V_$SHARED_POOL_RESERVED
V$SHARED_POOL_ADVICE Synonym for V_$SHARED_POOL_ADVICE
GV$SHARED_SERVER_MONITOR Synonym for GV_$SHARED_SERVER_MONITOR
GV$SHARED_SERVER Synonym for GV_$SHARED_SERVER
GV$SHARED_POOL_RESERVED Synonym for GV_$SHARED_POOL_RESERVED
GV$SHARED_POOL_ADVICE Synonym for GV_$SHARED_POOL_ADVICE
其中GV$開頭的視圖是用於集群中的,我們一般隻關心V$開頭的信息;可能你連表的大致意思都不清楚,你隻大致記得有一個字段大致的名稱,如我們知道一個視圖內部字段的名稱有一個以SCN開頭的列,那麼我們這樣也可以反向給它定位:
SQL> SELECT T.TABLE_NAME, T.COLUMN_NAME
2 FROM DICT_COLUMNS T
3 WHERE T.COLUMN_NAME LIKE 'SCN%';
TABLE_NAME COLUMN_NAME
------------------------------ ------------------------------
ALL_SUMDELTA SCN
DBA_AUDIT_TRAIL SCN
USER_AUDIT_TRAIL SCN
DBA_AUDIT_STATEMENT SCN
USER_AUDIT_STATEMENT SCN
DBA_AUDIT_OBJECT SCN
USER_AUDIT_OBJECT SCN
DBA_AUDIT_EXISTS SCN
DBA_FGA_AUDIT_TRAIL SCN
DBA_COMMON_AUDIT_TRAIL SCN
DBA_CAPTURE_PREPARED_TABLES SCN
ALL_CAPTURE_PREPARED_TABLES SCN
DBA_FILE_GROUP_TABLES SCN
ALL_FILE_GROUP_TABLES SCN
USER_FILE_GROUP_TABLES SCN
V$RESTORE_POINT SCN
V$RECOVERY_STATUS SCN_NEEDED
V$LOGMNR_CONTENTS SCN
V$XML_AUDIT_TRAIL SCN
GV$RESTORE_POINT SCN
這裏回到正題:清空共享池(OLTP係統運行時不要去操作,這個過程很影響整體運行):
SQL>ALTER SYSTEM FLUSH SHARED_POOL;
如果要查詢某過程或包的源碼,可以看一下係統的資源包:
SQL>SELECT * FROM USER_SOURCE t WHERE t.name = '過程或包的名字' ORDER BY LINE;
這些源碼信息在首次是不會裝入內存的,因為共享池的大小有限,調用時再裝入內存,而且也不會逃脫LRU的命運,若一些寫的很爛的SQL,就有可能把它替換出去,這個時候我們想做到的是啟動時直接裝入內存並不會被替換,ORACLE給我們一個KEEP方法,但是並非默認的,也就是安裝ORACLE後並不是默認就提供的這個包,如果你用具有DBA權限的人進去,不能使用DBMS_SHARED_POOL這個包(報:這個包不存在),說明還沒有創建,此時需要做一下操作,才能創建:
1、首先定位ORACLE_HOME的位置,我們沒有直接定位ORACLE_HOME的方式,除非是你自己安裝的,如果不知道,用下麵一個辦法:
SELECT * FROM V$PARAMETER P1
WHERE P1.NAME = 'spfile';
2、若沒有該目錄,使用CREATE SPFILE FROM PFILE;執行一下重啟OK就有了,得到該目錄後,假如得到如下:
D:/ORACLE10/PRODUCT/10.2.0/DB_1/DATABASE/SPFILEORCL102.ORA
那麼ORACLE_HOME上相推兩層得到:
D:/ORACLE10/PRODUCT/10.2.0/DB_1/
那麼要得到那個包的創建腳本就在:
D:/oracle10/product/10.2.0/db_1/RDBMS/ADMIN/DBMSPOOL.SQL
此時需要到安裝數據庫的機器上去執行,如果你本地有腳本當然也可以執行,但是注意:這個執行必須是在SQLPLUS中,PL/SQL中執行該腳本不好用。
3、執行方式:進入到安裝該數據庫的SQLPLUS下用SYS用戶登錄,該包需要創建在SYS用戶下。
SQL> @D:/oracle10/product/10.2.0/db_1/RDBMS/ADMIN/DBMSPOOL.SQL
程序包已創建。
授權成功。
視圖已創建。
程序包體已創建。
4、對於係統的大過程,可能第一次裝載比較緩慢,而且如果使用頻率較高,可以將其脫離LRU算法,並直接裝入內存,如果可以的話,做ORACLE啟動時觸發器,如果不行,就手動執行一下代碼:
BEGIN
SYS.DBMS_SHARED_POOL.KEEP('存儲過程或包的名字');
END;
若想將某過程從內存中去除掉:
BEGIN
SYS.DBMS_SHARED_POOL.UNKEEP('存儲過程或包的名字');
END;
5、此時查看緩衝池中是否裝載改對象:
SELECT name,owner,type
FROM v$db_object_cache where kept = 'YES'
AND NAME ='過程或包的名字';//這個地方也可以用SQL片段來LIKE
查看共享池中執行的一些SQL包頭:
SELECT * FROM V$SQLAREA;
通不過上述的SQL得到HASH_VALUE或者ADDREDSS或者SQL_ID都可以通過以下視圖得到對應執行SQL的全部內容(當SQL較長的時候,V$SQLAREA隻保存前麵一部分,全部內容在該視圖中):
SELECT * FROM V$SQLTEXT_WITH_NEWLINES;
得到SQL的執行計劃:
SELECT * FROM V$SQL_PLAN;
得到對共享池設置的建議值,ORACLE根據實際運行情況,推薦值:
SELECT * FROM V$SHARED_POOL_ADVANCE;
還有些不是很常用的:
得到SQL綁定變量信息:
SELECT * FROM V$SQL_BIND_CAPTURE;
SQL占用共享池內存:
SELECT * FROM V$SQL_SHARED_MEMORY;
SQL消耗調用的統計信息:
SELECT * FROM V$SQLSTATS;
這裏隻是由預編譯->共享池->SGA的過程,對於SGA的內核隻是闡述了共享池的部分,下次說明SGA的另一大塊,Data Buffer,數據緩衝區,該區域在OLTP係統中非常重要。
最後補充話題,本來這個想在後麵說的,因為涉及一些其他內容,不過既然說到,就提一下吧,我們在OLTP要求使用綁定參數方式執行SQL如:
用應用程序的SQL應當是:
String sql = “SELECT * FROM A WHERE ID=?”;
而不是:
String sql = “SELECT * FROM A WHERE ID=”+id;
那麼這樣的情況我們該怎麼辦呢?當要查詢多個ID,使用IN的情況,或者同時修改多條記錄的操作,我們無疑想出最常規的三種辦法(我們先介紹常規方法,再介紹解決問題的方法):
方法1(拚串,放棄預編譯):
String sql = “SELECT * FROM A WHERE ID IN(”+keys+”)”;
付:該方法放棄預編譯,但是也是常規方法中的無奈之舉。
方法2(將參數個數動態化去預編譯)
StringBuffer sql = new StringBuffer(256);
sql.append(“SEELCT * FROM A WHERE ID IN(”);
for(….) {
sql.append(“?”).append(“,”);
}
sql.deleteCharAt(sql.length()-1);
付:該方法比上一種稍微好一點,OLTP下一般情況下,我們常規方法中最少要這樣去完成,大家可以把ibatis的執行SQL日誌拿出來看下即可發現,ibatis對於動態參數個數也是這樣去完成的,對於並發度不算高的代碼段我們可以這樣使用,如果並發度高的代碼段,這樣使用我們也不會考慮。
方法3(循環提取。循環修改)
for(….) {
ptmt.setInt(1,ID[i]);
ptmt.executeQuery(“SELECT * FROM A WHERE ID=?”);
}
付:這在執行過程中往往是最“不應該使用”的辦法了;如果執行插入操作,我們會適當考慮攜帶批處理這樣去完成也是可以的,不過對於UPDATE和SELECT這類操作我們不該這樣使用的。
在這裏上述三種辦法,隻有第二種方法在OLTP中並發量不大的情況下可以使用,若並發量較大,且參數個數的動態性比較大,也應該考慮使用其他方法去實現,因為大家通過上述試驗和反向查詢後發現,問號個數的變化也會產生不同的SQL,共享池中仍然會造成很多的垃圾,隻是相對第一種方法概率降低了很多,而第三種方法基本是我們不考慮的。
我們說一下如果對於這樣的情況,批量執行過程中,我們該如何轉換,利用ORACLE的數組進行轉換,為此我們先在ORACLE內部提供一個函數,和數組類型,前序工作:
步驟1:
創建數據類型(表格類型,也類似數組):
CREATE OR REPLACE TYPE MY_TABLE_TYPE IS TABLE OF VARCHAR2(8000);
步驟2:
創建轉換函數(在網上很多地方可以找到類似代碼,我這隻是一個參考):
CREATE OR REPLACE FUNCTION SPLIT(SRC_STR IN VARCHAR2,
SPLIT_STR VARCHAR2) RETURN MY_TABLE_TYPE IS
V_TABLE_STR MY_TABLE_TYPE := MY_TABLE_TYPE();
V_TEMP_STR VARCHAR2(8000) := SRC_STR;
V_SPLIT_STR VARCHAR2(20) := SPLIT_STR;
I NUMBER := 1;
J NUMBER := 1;
BEGIN
IF V_SPLIT_STR IS NULL THEN
V_SPLIT_STR := ',';--我們默認用逗號分隔
END IF;
IF SRC_STR IS NULL OR SRC_STR = V_SPLIT_STR THEN
RETURN V_TABLE_STR;
END IF;
V_TEMP_STR := LTRIM(V_TEMP_STR, V_SPLIT_STR);
LOOP
I := INSTR(V_TEMP_STR, V_SPLIT_STR, J);
EXIT WHEN I = 0 OR J > LENGTH(V_TEMP_STR);
V_TABLE_STR.EXTEND;
V_TABLE_STR(V_TABLE_STR.COUNT) := SUBSTR(V_TEMP_STR, J, I - J);
J := I + LENGTH(V_SPLIT_STR);
END LOOP;
IF J < LENGTH(V_TEMP_STR) THEN
V_TABLE_STR.EXTEND;
V_TABLE_STR(V_TABLE_STR.COUNT) := SUBSTR(V_TEMP_STR, J, LENGTH(V_TEMP_STR) - J + 1);
END IF;
RETURN V_TABLE_STR;
END SPLIT;
步驟3:(測試可用性,這裏假如數據都是按照逗號分隔的)
較高版本支持這樣的寫法:
SQL> SELECT * FROM TABLE(SPLIT('123,321',','));
COLUMN_VALUE
--------------------------------------------------------------------------------
123
321
較低版本可以這樣寫:
SQL> SELECT * FROM TABLE(CAST(SPLIT('123,321,456', ',') AS MY_TABLE_TYPE));
COLUMN_VALUE
--------------------------------------------------------------------------------
123
321
456
步驟4(程序應用):
String sql = “SELECT * FROM A WHERE ID IN(SELECT * FROM TABLE(SPLIT(?,',')))”;//當然對於低版本的數據庫,相應修改即可。
但是ORACLE有些時候會很傻的去使用HASH JOIN,因為他們他不知道你裏麵返回多少數據,而我們通過轉換回來的ID往往數據量很少,最多就是幾十行上百行,若目標表為一個大表,使用HASH JOIN的確是一件很浪費的事情,此時我們不願意這樣去做,因為很浪費CPU和臨時表空間(這其實是後麵要說的),我們一般需要強製指定查詢的方式來控製他走嵌套循環,讓大表根據小表去走索引,使用ORACLE的Hint來強製告訴它應該由小表引導大表執行,來保證SQL執行計劃的穩定性:
String sql = “SELECT /*+ordered use_nl(a2,a1)*/a2.* FROM A a1,(SELECT COLUMN_VALUE FROM TABLE(SPLIT(?,',')) a2 WHERE a1.ID = A2.COLUMN_VALUE”;
此時可能會問,這樣轉一次會不會很慢,是的,這不難會想想一個拆開字符串的過程,我們必然會消耗一點,不過要想到一次執行就是拆開一個字符串而已,而且我們傳入的字符串也不會太長,這個解析過程對於ORACLE來說還是沒啥問題的,不必擔心這個,而更加重要的提高了共享池的應用。
最後更新:2017-04-02 05:21:03
上一篇:
gcc下的一個VC匯編轉換
下一篇:
ajax +jsp+iframe無刷新上傳文件
技術人員談管理之進度管理案例論文
生產者消費者問題-轉自維基百科
MongoDB權限管理代碼分析
起源自天文學的PostgreSQL 優化器成本因子校對
物聯網技術開發的冰箱內裝攝影機 可消費者不買賬
基於分布式數據庫的存儲和hadoop的分布式計算的分布式sql計算方法
秒懂!看機器學習如何淨化處理文本
《Spring Data實戰》——1.4 示例代碼
Interop type 'Microsoft.Office.Interop.Word.ApplicationClass' cannot be embedded. Use the applicable
怡海軟件:關於雲計算,未來的三個預測