前 言
在系統開發中,經常有這樣的需求:前臺傳入一個字符串,而且此字符串具有指定分隔符,并且長度不定,那么如何根據傳入的這個字符串查詢對應的結果呢?考慮這樣的需求,你肯定已經想到,采用構造SQL語句來解決,的確,你的想法沒錯,最簡單的也就是構造SQL:
--將字符串轉換為convert_value_list,convert_
value_list類似于值的列表,比如將a,b,c轉為a,b,c
SELECT ….. FROM ….. WHERE column in (convert_value_list);
的確可以通過構造SQL來解決這樣的問題(比如在JAVA中可以將傳入的字符串通過String的split方法處理,然后將結果拼湊到SQL中),但是另一方面,這樣的寫法有一定的限制:Oracle WHERE條件中IN列表數目不能超過1000個,另外列表數目不定會導致無法使用綁定變量而影響效率。
那么怎樣才能使列表長度不定而又能使用綁定變量呢?解決方法有很多種,下面逐一分析,從而使你能夠根據實際情況來選擇何種方法(動態SQL構造也會作為例子進行反面探討,這個例子在PL/SQL中實現,當然在JAVA等語言中實現方式也類似)。
深入解析
解決binging in list問題,首要考慮的兩個問題就是解決列表長度問題和效率問題,效率問題首要考慮綁定變量問題,另外還要考慮比如cardinality(基數)對執行計劃的影響等。
為了避免復雜性,測試的大部分例子只根據binging in list計算COUNT。
建立測試表:
--建兩個表xy和yz作為測試用,所以這兩個表很簡單,不需要數據。
DROP TABLE xy;
CREATE TABLE xy(id NUMBER);
DROP TABLE yz;
CREATE TABLE yz(id NUMBER);
--將all_objects copy到表t中,以便測試。
DROP TABLE t;
CREATE TABLE t AS SELECT * FROM all_objects;
CREATE INDEX idx_t ON t(object_name);
--分析表和索引。
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => USER,tabname => t);
DBMS_STATS.GATHER_INDEX_STATS(ownname => USER,indname => idx_t);
END;
/
本節主要研究動態SQL解決binging in list問題以及相關分析。
下面使用一個簡單的拼湊條件進行初步試驗,這里我使用的SQL是靜態SQL,看看會發生什么情況?
DINGJUN123>SET SERVEROUTPUT ON SIZE 10000
DINGJUN123>DECLARE
2 v_condition VARCHAR2(100);
3 v_sql VARCHAR2(1000);
4 v_count NUMBER(10);
5 BEGIN
6 v_condition := XY || , || YZ; --本意是拼湊XY,YZ,有很多人會寫成XY,YZ
7 SELECT COUNT(*)
8 INTO v_count
9 FROM t
10 WHERE object_name IN (v_condition);
11 --打印結果
12 DBMS_OUTPUT.PUT_LINE(v_count);
13 --打印SQL
14 v_sql := SELECT COUNT(*) FROM t WHERE object_name IN ( ||
15 v_condition || );
16 DBMS_OUTPUT.PUT_LINE(v_sql);
17 END;
18 /
0
SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ)
從上面的結果看到,通過拼湊的SQL,打印出來的是SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ),看似正確的,但是為什么執行結果是0呢?下面分析一下,執行此SQL:
DINGJUN123>SELECT COUNT(*)
2 FROM t
3 WHERE object_name IN (XY,YZ);
COUNT(*)
----------
2
已選擇 1 行。
的確是有結果的,但是為什么在PL/SQL中執行拼湊的靜態SQL沒有結果呢?原因在于在PL/SQL中打印出的SQL不是真正執行的SQL,打印的是動態拼湊的SQL,而真正執行的是靜態SQL,注意:
SELECT COUNT(*) INTO v_count FROM t WHERE object_name IN (v_condition);
v_condition是一個varchar2類型,在靜態SQL中拼湊的條件相當于一個整體,XY,YZ是一個字符串,在SQL中相當于XY,YZ,因此實際執行的SQL是:
SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ); //返回0
而不是:
SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ); //返回2
因此沒有找到數據。
這個錯誤很多人初次解決類似問題會碰到,而且可能百思不得其解,通過上面的分析,你可能已經發現靜態SQL與動態SQL有很多不同的地方值得注意。
使用動態SQL,就可以正確查詢結果了,如下:
DINGJUN123> DECLARE
2 v_condition VARCHAR2(100);
3 v_sql VARCHAR2(1000);
4 v_count NUMBER(10);
5 BEGIN
6 v_condition:=XY||,||YZ;
7 --放入動態SQL中,結果正確
8 v_sql:=SELECT COUNT(*) FROM t WHERE object_name IN (||v_condition||);
9 EXECUTE IMMEDIATE v_sql INTO v_count;
10 DBMS_OUTPUT.PUT_LINE(v_count);
11 DBMS_OUTPUT.PUT_LINE(v_sql);
12 END;
13 /
2
SELECT COUNT(*) FROM t WHERE object_name IN (XY,YZ)
PL/SQL 過程已成功完成。
現在的結果和預期結果一致,查詢返回2。動態SQL的確可以解決這個問題,但是動態SQL會拼湊很多常量,而且數目不定,會導致無法使用綁定變量而影響效率(可能你認為可以使用動態SQL的USING,這是不行的,因為你不知道要綁定多少變量),而且IN中列表數目限制最大是1000。所以,針對這種方法,在實際開發中不推薦使用。
Oracle 10G支持正則表達式的確給程序開發帶來了很大方便,正則表達式是字符串處理的利器,Perl、JAVA、JAVASCRIPT等主流開發語言都支持正則表達式,Oracle也意識到正則表達式的重要性,所以在10G中也引入了對正則表達式的支持。在本節中將使用正則表達式REGEXP_SUBSTR,將按指定分隔符組成的字符串轉為中間查詢結果集,然后使用子查詢(IN、EXISTS)或JOIN解決binging in list問題,當然一般都是使用IN子查詢,因為一般不可能選擇很多個值。對于正則表達式,如需詳細了解,請參考官方文檔,使用正則表達式解決binging in list問題的方法如下:
DINGJUN123>VAR str VARCHAR2(100);
DINGJUN123>EXEC :str := XY,YZ;
PL/SQL 過程已成功完成。
DINGJUN123>SELECT COUNT(*)
2 FROM t
3 WHERE object_name IN
4 (
5 SELECT REGEXP_SUBSTR(:str, [^,]+, 1, LEVEL) AS value_str
6 FROM DUAL
7 CONNECT BY LEVEL <=
8 LENGTH(TRIM(TRANSLATE(:str,TRANSLATE(:str, ,, ), ))) + 1
9 );
COUNT(*)
----------
2
已選擇 1 行。
上面的SQL使用REGEXP_SUBSTR將逗號分隔的字符串轉為行結果集,其中LENGTH(TRIM(TRANSLATE(:str,TRANSLATE(:str, ,, ), ))) + 1就是查詢出有多少個值列表(注意 是空格),當然,也可以使用LENGTH(:str)-LENGTH(REPLACE(:str,,,))+1實現(這里的是空字符串,相當于NULL),這種方法在10G環境中的大部分情況下是可以使用的,好處是可以用到綁定變量,而且列表數可以超過1000個。這里的正則表達式的子查詢還是有點復雜的,下一節會講解如何將子查詢封裝為一個動態視圖,從而屏蔽子查詢的復雜性。
上一節使用正則表達式解決binging in list問題,但是如果你的Oracle版本較低(10G之前),無法使用正則表達式怎么辦?那么就可以用本節的知識來解決了,使用INSTR、SUBSTR等函數處理指定分隔符的字符串,將字符串按分隔符轉為行,這樣就可以像上一節那樣處理了。
首先要解決的問題就是如何使用INSTR、SUBSTR等函數將字符串按分隔符轉為多行記錄,比如對于ab,bc,cd這個字符串來說,要轉為3行記錄分別為ab、bc、cd,如何轉換呢?一般要用到CONNECT BY的,試想如果將字符串轉為,ab,bc,cd,,那么就很好轉換了,找第1個值ab就是從第1個逗號后面的位置開始,然后截取的長度就是第2個逗號位置-第1個逗號位置-1,其他值類似,有了這個分析,就能很好實現這個需求了:
DINGJUN123>VAR str VARCHAR2(100);
DINGJUN123>EXEC :str := ab,bc,cd;
PL/SQL 過程已成功完成。
-- LENGTH(:str)-LENGTH(REPLACE(:str,,,))+1是計算有多少個值,和前一節的TRANSLATE一樣。
DINGJUN123>SELECT
2 SUBSTR (inlist,
3 INSTR (inlist, ,, 1, LEVEL ) + 1,
4 INSTR (inlist, ,, 1, LEVEL+1)
5 - INSTR (inlist, ,, 1, LEVEL) -1 )
6 AS value_str
7 FROM (SELECT ,||:str||, AS inlist
8 FROM DUAL)
9 CONNECT BY LEVEL <=
10 LENGTH(:str)-LENGTH(REPLACE(:str,,,)) + 1;
VALUE_STR
------------------------------------------------------------
ab
bc
cd
已選擇3行。
有了上面的結果作為子查詢就和正則表達式一樣可以解決binging in list問題,在上一節我說過,這樣的子查詢可能會很復雜,為了隱藏子查詢的復雜性,可以將子查詢封裝為一個動態視圖,所謂動態視圖就是傳入不同的字符串,視圖的結果是不同的,那么如何實現動態視圖功能呢?
在PL/SQL中有內置包DBMS_SESSION,這個包的方法SET_CONTEXT可以創建綁定名字的上下文,并且具有屬性名和屬性值,通過SYS_CONTEXT函數就可以獲取指定上下文的屬性值。這樣只要視圖中的字符串值是通過SYS_CONTEXT獲取的就可以了,每次調用存儲過程重置CONTEXT。注意創建CONTEXT必須在一個命名過程或包過程中調用DBMS_SESSION.SET_CONTEXT,而不能在匿名過程中直接使用DBMS_SESSION.SET_CONTEXT,對于DBMS_SESSION包的詳細使用請參考相關文檔。詳細如下:
3.1 創建上下文
--這個上下文的名字是INLIST_CTX,需要由過程SET_INLIST_CTX_PRC創建。
DINGJUN123>CREATE OR REPLACE CONTEXT INLIST_CTX USING set_inlist_ctx_prc;
上下文已創建。
3.2 建立與上下文創建相關的過程
DINGJUN123>CREATE OR REPLACE PROCEDURE set_inlist_ctx_prc(p_val IN VARCHAR2)
2 /**
3 ||程序說明:
4 ||上下文INLIST_CTX屬性名為STR
5 ||p_val為屬性對應的值
6 **/
7 AS
8 BEGIN
9 DBMS_SESSION.set_context(INLIST_CTX, STR, p_val);
10 END;
11 /
過程已創建。
3.3 建立視圖
--創建動態視圖,讓SYS_CONTEXT動態給視圖傳參,只需要將前面語句中的綁定變量:str改為SYS_CONTEXT(INLIST_CTX, STR)就可以了。
DINGJUN123>CREATE OR REPLACE VIEW v_inlist
2 AS
3 SELECT
4 SUBSTR (inlist,
5 INSTR (inlist, ,, 1, LEVEL ) + 1,
6 INSTR (inlist, ,, 1, LEVEL+1)
7 - INSTR (inlist, ,, 1, LEVEL) -1 )
8 AS value_str
9 FROM (SELECT ,||SYS_CONTEXT(INLIST_CTX, STR)||,
10 AS inlist
11 FROM DUAL)
12 CONNECT BY LEVEL <=
13 LENGTH(SYS_CONTEXT(INLIST_CTX, STR))
14 -LENGTH(REPLACE(SYS_CONTEXT(INLIST_CTX, STR),,,))+1;
視圖已創建。
3.4 測試
下面測試此動態視圖,看是否滿足要求:
--創建上下文,并給予屬性STR初始值為ab,bc,cd;
DINGJUN123>EXEC set_inlist_ctx_prc(ab,bc,cd);
PL/SQL 過程已成功完成。
--視圖成功輸出3行記錄:
DINGJUN123>SELECT value_str
2 FROM v_inlist;
VALUE_STR
--------------
ab
bc
cd已選擇3行。
--修改上下文的屬性值,則視圖也改變:
DINGJUN123>EXEC set_inlist_ctx_prc(x,y,z);
PL/SQL 過程已成功完成。
DINGJUN123>SELECT value_str
2 FROM v_inlist;
VALUE_STR
---------------
x
y
z
已選擇3行。
通過測試發現,動態視圖正常工作,而且因為保存在CONTEXT內的屬性是在SESSION范圍內的,具有很好的并發性。
下面就用這個動態視圖實現本章討論的binging in list問題,其實很簡單,只要將視圖放入到子查詢中即可,如下:
--先重置CONTEXT
DINGJUN123>EXEC set_inlist_ctx_prc(XY,YZ);
PL/SQL 過程已成功完成。
DINGJUN123>SELECT COUNT(*)
2 FROM t
3 WHERE object_name IN
4 (
5 SELECT value_str
6 FROM v_inlist
7 );
COUNT(*)
----------
2
已選擇 1 行。
這個查詢是符合要求的,使用動態視圖,可以隱藏查詢的復雜性,只需要每次查詢前調用存儲過程重置CONTEXT即可,而且和正則表達式一樣,列表數目可以不定,也使用到了綁定變量。本節主要討論使用INSTR+SUBSTR代替正則表達式在低版本Oracle中的使用,并且介紹了使用DBMS_SESSION包創建CONTEXT和建立動態視圖放入子查詢中隱藏查詢復雜性的方法。
對這類問題的常規解法,比如Oracle版本是9i,可以使用PL/SQL中的集合類型,對傳入的字符串按分隔符解析之后存儲到相關集合類型的變量中,比如可以存儲到嵌套表,數組中(注意不能是INDEX BY表,必須是SCHEMA級別的類型,數組有容量也不常使用),然后利用TABLE函數將集合轉為偽表,剩下就和前面說的一樣了。
試想一下,使用集合構造臨時表,需要做哪些工作呢?
4.1 外界傳入的是一個含有分隔符(一般是逗號,確保字段中沒有逗號,如果有,用其他分隔符)的字符串,比如’aa,bb,cc’之類的字符串,首先需要按分隔符解析,然后將每個值存儲到對應的集合變量中,所以,需要有一個函數能夠接收傳入的字符串,然后解析并存儲到相應的集合變量并且返回。
4.2 將集合變量通過TABLE函數轉換為偽表,放到子查詢中。table函數將集合轉為偽表,返回的列名是COLUMN_VALUE,對應的類型是集合元素的類型,本節例子的COLUMN_VALUE的類型就是VARCHAR2類型。
4.3 然后寫相應的查詢語句。
根據上面的描述,需要創建嵌套表以及將字符串轉為嵌套表的函數,如下所示:
--創建嵌套表:
CREATE OR REPLACE TYPE varchar2_tt AS TABLE
OF VARCHAR2 (1000);
/
--創建函數:
CREATE OR REPLACE
FUNCTION f_str2list( in_str IN VARCHAR2 ,in_delimiter IN
VARCHAR2 DEFAULT , )
RETURN varchar2_tt
/******************************************************************************
||程序說明:將按指定分隔符分割的字符串轉為嵌套表類型變量返回
||輸入變量:
|| in_str 字符串,如a,b,c
|| in_delimiter 分割符,默認是逗號
||輸出變量:
|| varchar2_tt類型,嵌套表
******************************************************************************/
AS
v_str VARCHAR2(32767) DEFAULT in_str ||in_delimiter;
v_result varchar2_tt := varchar2_tt();
i NUMBER;
BEGIN
LOOP
EXIT WHEN v_str IS NULL;
i := INSTR( v_str, in_delimiter );
v_result.extend;
v_result(v_result.count) :=
TRIM( SUBSTR( v_str, 1, i -1 ) );
v_str := SUBSTR( v_str, i +1 );
END LOOP;
RETURN v_result;
END;
/
執行上面的腳本,創建嵌套表和返回嵌套表的函數即可。
下面使用上面創建的嵌套表和函數來解決binging in list的問題。通過函數將傳入的字符串包裝成嵌套表,然后利用TABLE函數將嵌套表轉為偽表,放到子查詢中即可。具體操作如下:
DINGJUN123>VAR str VARCHAR2(100);
DINGJUN123>EXEC :str :=XY,YZ;
PL/SQL 過程已成功完成。
DINGJUN123>SELECT COUNT(*)
2 FROM t
3 WHERE object_name IN
4 (SELECT column_value
5 FROM TABLE( CAST(f_str2list(:str) AS varchar2_tt )
6 )
7 );
COUNT(*)
----------
2
已選擇 1 行。
結果是正確的。如果傳入的不是字符串,而是一個SQL語句或REF CURSOR變量,可以嗎?當然可以,把f_str2list函數改改就可以了,這個讀者可以自己思考一下,這里不詳細講解。下節討論binging in list需要注意的性能問題。
其中 “4. 使用集合構造偽表” 中是使用普通的集合函數解決in list問題,其實使用管道函數更好,因為管道函數采用流的形式實時傳輸數據,這樣不需要等集合結果全部完成即可計算。將 “4. 使用集合構造偽表” 的函數改為管道函數即可:
CREATE OR REPLACE
FUNCTION f_str2list( in_str IN VARCHAR2 ,in_delimiter IN VARCHAR2 DEFAULT , )
RETURN varchar2_tt PIPELINED
AS
v_str VARCHAR2(32767) DEFAULT in_str ||
in_delimiter;
i NUMBER;
BEGIN
LOOP
EXIT WHEN v_str IS NULL;
i := INSTR( v_str, in_delimiter );
PIPE ROW (TRIM(SUBSTR(v_str, 1, i - 1)));
v_str := SUBSTR(v_str, i + 1);
END LOOP;
RETURN;
END;
/
測試結果完全一樣:
dingjun123@ORADB> SELECT count(*)
2 FROM t
3 WHERE object_name IN
4 (SELECT column_value
5 FROM TABLE( CAST(f_str2list(:str) AS varchar2_tt )
6 )
7 );
COUNT(*)
----------
2
Binging in list問題要特別注意性能問題,一般選擇的字段都建有索引,希望通過nested loop方式連接查詢,而不希望通過hash join或sort merge join方式連接查詢,因為實際中傳入的一般都不是很長的字符串。本部分主要探討使用集合函數解決binging in list問題中注意的相關問題,對于正則表達式和INSTR+SUBSTR也可以通過hint來固定計劃,比較簡單,所以只探討集合函數的性能問題,請看:
DINGJUN123>SELECT COUNT(*) FROM t;
COUNT(*)
----------
14006
已選擇 1 行。
DINGJUN123>SET AUTOTRACE TRACEONLY
DINGJUN123>VAR str VARCHAR2(100);
DINGJUN123>EXEC :str :=XY,YZ;
PL/SQL 過程已成功完成。
DINGJUN123> SELECT *
2 FROM t
3 WHERE object_name IN
4 (SELECT column_value
5 FROM TABLE( CAST( f_str2list(:str) AS varchar2_tt ))
6 );
已選擇2行。
執行計劃:
----------------------------------------------------------
Plan hash value: 3487633200
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 88 | 84 (3)| 00:00:02 |
|* 1 | HASH JOIN RIGHT SEMI | | 1 | 88 | 84 (3)| 00:00:02 |
| 2 | COLLECTION ITERATOR PICKLER FETCH| F_STR2LIST | | | | |
| 3 | TABLE ACCESS FULL | T | 14006 | 1176K| 54 (2)| 00:00:01 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("OBJECT_NAME"=VALUE(KOKBF$))
統計信息:
----------------------------------------------------------
927 recursive calls
0 db block gets
486 consistent gets
233 physical reads
0 redo size
1257 bytes sent via SQL*Net to client
384 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
8 sorts (memory)
0 sorts (disk)
2 rows processed
第1次硬解析,為了比較再次執行,統計信息為:
----------------------------------------------------------
0 recursive calls
0 db block gets
184 consistent gets
0 physical reads
0 redo size
1257 bytes sent via SQL*Net to client
384 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2 rows processed
從上面結果看到,上面SQL采用的是Hash join的連接方式,全表訪問表t,第1次執行邏輯讀很大,為486,第2次再執行,邏輯讀為184,平均每行邏輯讀為92(184/2),這種計劃是很差的。那為什么Oracle會采用這種計劃呢?如下分析:
DINGJUN123>SELECT/*+first_rows*/ *
2 FROM TABLE( CAST( f_str2list(:str) AS varchar2_tt ));
已選擇2行。
執行計劃:
----------------------------------------------------------
Plan hash value: 2025561284
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8168 | 16336 | 29 (0)| 00:00:01 |
| 1 | COLLECTION ITERATOR PICKLER FETCH| F_STR2LIST | | | | |
------------------------------------------------------------------------------------------------
從上面結果看出,TABLE函數的默認行數是8168行(TABLE函數創建的偽表是沒有統計信息的),這個值不小了,一般比實際應用中的行數要多的多,經常導致執行計劃走hash join,而不是nested loop。怎么改變這種情況呢?當然是加hint提示來改變執行計劃了,對binging in list,常常使用的hint有:first_rows,index,cardinality等。這里特別介紹下cardinality(table|alias,n),這個hint很有用,它可以讓CBO優化器認為表的行數是n,這樣就可以改變執行計劃了。現在改寫上面的查詢:
DINGJUN123>SELECT/*+cardinality(tab,5)*/ column_value
2 FROM TABLE( CAST( f_str2list(:str) AS varchar2_tt )) tab;
已選擇2行。
執行計劃:
----------------------------------------------------------
Plan hash value: 2025561284
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 10 | 29 (0)| 00:00:01 |
| 1 | COLLECTION ITERATOR PICKLER FETCH| F_STR2LIST | | | | |
------------------------------------------------------------------------------------------------
加了cardinality(tab,5)自動走CBO優化器了,優化器把表的基數看成5,前面的binging in list查詢基數默認為8168的時候走的是hash join,現在有了cardinality,測試如下:
DINGJUN123> SELECT *
2 FROM t
3 WHERE object_name IN
4 (SELECT /*+cardinality(tab,5)*/ column_value
5 FROM TABLE( CAST( f_str2list(:str) ASvarchar2_tt )) tab
6 );
已選擇2行。
執行計劃:
----------------------------------------------------------
Plan hash value: 4129437246
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 528 | 36 (3)| 00:00:0
| 1 | TABLE ACCESS BY INDEX ROWID | T | 1 | 86 | 2 (0)| 00:00:0
| 2 | NESTED LOOPS | | 6 | 528 | 36 (3)| 00:00:0
| 3 | SORT UNIQUE | | | | |
| 4 | COLLECTION ITERATOR PICKLER FETCH| F_STR2LIST | | | |
|* 5 | INDEX RANGE SCAN | IDX_T | 1 | | 1 (0)| 00:00:0
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("OBJECT_NAME"=VALUE(KOKBF$))
統計信息:
----------------------------------------------------------
590 recursive calls
0 db block gets
149 consistent gets
14 physical reads
0 redo size
1257 bytes sent via SQL*Net to client
384 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
2 rows processed
看下第2次軟解析的統計信息:
----------------------------------------------------------
0 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
1257 bytes sent via SQL*Net to client
384 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
2 rows processed
第1次邏輯讀為149,比前面hash join的軟解析邏輯讀還要少,而且第2次邏輯讀為7,則平均每行邏輯讀為3.5,效率很好。現在計劃走nested loop了,而且對表t也走了索引
總 結
對于前臺傳入帶分隔符的動態字符串作為條件拼湊SQL,需要考慮到綁定變量問題,ORACLE可采用多種方法避免硬解析,常用的就是采用集合函數解決,需要注意的是集合函數默認cardinality:8168導致SQL不能走NESTED LOOPS和索引的問題。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/129691.html
摘要:音樂團隊分享數據綁定運行機制分析一個項目搞定所有主流架構單元測試一個項目搞定所有主流架構系列的第二個項目。代碼開源,展示了的用法,以及如何使用進行測試,還有用框架對的進行單元測試。 Android 常用三方框架的學習 Android 常用三方框架的學習 likfe/eventbus3-intellij-plugin AS 最新可用 eventbus3 插件,歡迎品嘗 簡單的 MVP 模...
摘要:開始重現客戶端指令其實這次請求的一些貓膩很容易就能發現在中有空格。而在函數中,做的主要事情就是來解析數據包,在解析完成后執行一下回調函數。具體的一些回調函數就不細講了,有興趣的童鞋可自行翻閱。如代碼片段所示,前文中所對應的函數就是了。 本文首發于知乎專欄螞蟻金服體驗科技。 首先聲明,我在Bug字眼上加了引號,自然是為了說明它并非一個真 Bug。 問題拋出 昨天有個童鞋在看后臺監控的時候...
摘要:僅對于組件,用于監聽原生事件,而不是組件內部使用觸發的事件。注意,你無法對中的賦值,因為已經自動為你進行了同步。 簡介 在使用Vue進行開發的時候,大多數情況下都是使用template進行開發,使用template簡單、方便、快捷,可是有時候需要特殊的場景使用template就不是很適合。因此為了很好使用render函數,我決定深入窺探一下。各位看官如果覺得下面寫的有不正確之處還望看官...
摘要:雖然它不是必須,但是它是個很好的輔助官方解釋首先看看官方的對它怎么說它將方法參數方法返回值綁定到的里面。解析注解標注的方法參數,并處理標注的方法返回值。 每篇一句 我們應該做一個:胸中有藍圖,腳底有計劃的人 前言 Spring MVC提供的基于注釋的編程模型,極大的簡化了web應用的開發,我們都是受益者。比如我們在@RestController標注的Controller控制器組件上用@...
閱讀 1346·2023-01-11 13:20
閱讀 1684·2023-01-11 13:20
閱讀 1132·2023-01-11 13:20
閱讀 1858·2023-01-11 13:20
閱讀 4100·2023-01-11 13:20
閱讀 2704·2023-01-11 13:20
閱讀 1385·2023-01-11 13:20
閱讀 3594·2023-01-11 13:20