ORACLE資料庫中,我們會使用一些SQL語句找出存在隱式轉換的問題SQL,其中網上流傳的一個SQL語句如下,查詢V$SQL_PLAN的欄位FILTER_PREDICATES中是否存在INTERNAL_FUNCTION: SELECT SQL_ID, PLAN_HASH_VALUEFROM V$SQ... ...
ORACLE資料庫中,我們會使用一些SQL語句找出存在隱式轉換的問題SQL,其中網上流傳的一個SQL語句如下,查詢V$SQL_PLAN的欄位FILTER_PREDICATES中是否存在INTERNAL_FUNCTION:
SELECT
SQL_ID,
PLAN_HASH_VALUE
FROM
V$SQL_PLAN X
WHERE
X.FILTER_PREDICATES LIKE '%INTERNAL_FUNCTION%'
GROUP BY
SQL_ID,
PLAN_HASH_VALUE;
但是筆者測試驗證發現,有時候,執行計劃中出現INTERNAL_FUNCTION,並不一定代表出現了隱式數據類型轉換,下麵我們結合這篇博客“What the heck is the INTERNAL_FUNCTION in execution plan predicate section?”來講述一下執行計劃謂詞部分中的INTERNAL_FUNCTION到底是什麼?這篇博客沒有打算直接翻譯這篇文章,而是想結合自己的理解,來簡單講述一下INTERNAL_FUNCTION。其實官方文檔對INTERNAL_FUNCTION的介紹非常少,最常見的理解,INTERNAL_FUNCTION這種特殊函數用於執行隱式數據類型轉換(implicit datatype conversion),可能來自官方文檔https://docs.oracle.com/cd/E11882_01/server.112/e25523/part_avail.htm#sthref141 。但是這個說法,事實上僅僅部分正確,而不是全部的事實。事實上,ORACLE中找不到INTERNAL_FUNCTION這個函數,通過V$SQLFN_METADATA視圖根本找不到INTERNAL_FUNCTION這個對象。
COL sqlfn_descr HEAD DESCRIPTION FOR A100 WORD_WRAP
COL sqlfn_name HEAD NAME FOR A30
SELECT
func_id
, name sqlfn_name
, offloadable
-- , usage
, minargs
, maxargs
-- this is just to avoid clutter on screen
, CASE WHEN name != descr THEN descr ELSE null END sqlfn_descr
FROM
v$sqlfn_metadata
WHERE
UPPER(name) LIKE UPPER('%&1%')
/
一般而言,我們在執行計劃的的謂詞部分發現出現“INTERNAL_FUNCTION”,那麼可能意味著出現了隱式類型轉換(implicit data type conversion),下麵我先簡單構造一個例子,
SQL> CREATE TABLE t(a VARCHAR2(20), b DATE);
Table created.
SQL> INSERT INTO t VALUES( TO_CHAR(sysdate), sysdate) ;
1 row created.
SQL> commit;
Commit complete.
如下所示,這個SQL會出現隱式數據類型轉換(implicit datatype conversion)
SQL> SELECT * FROM t WHERE a = b;
no rows selected
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID 4ptcbny27y9b0, child number 0
-------------------------------------
SELECT * FROM t WHERE a = b
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 2 (100)| |
|* 1 | TABLE ACCESS FULL| T | 1 | 21 | 2 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("B"=INTERNAL_FUNCTION("A"))
Note
-----
- dynamic sampling used for this statement
22 rows selected.
通過執行計劃,我們看到ORACLE為了能夠比較兩個不同數據類型(欄位A與B之間的比較),強制在欄位A上加了一個數據類型轉換函數,在ORACLE內部,運算從WHERE a=b 轉換為WHERE TO_DATE(a)=b, 這也是為什麼執行計劃中出現INTERNAL_FUNCTION的原因-從實際的“二進位”執行計劃生成可讀性的執行計劃的代碼無法將內部操作碼轉換為相應的適合人們容易理解的函數名稱,因此預設使用“INTERNAL_FUNCTION”字元串取而代之顯示。 英文原文如下,可以對比理解(如果覺得翻譯的不好的話)
What happens here is that Oracle is forced to (implicitly) add a datatype conversion function around column A, to be able to physically compare two different datatypes. Internally Oracle is not running a comparison <strong>"WHERE a = b"</strong> anymore, but rather something like <strong>"WHERE TO_DATE(a) = b"</strong>. This is one of the reasons why the INTERNAL_FUNCTION shows up – the code generating the human-readable execution plan from the actual “binary” execution plan is not able to convert the internal opcode to a corresponding human-readable function name, thus shows a default “INTERNAL_FUNCTION” string there instead.
Un-unparseable Complex Expressions
執行計劃中出現“INTERNAL_FUNCTION”,還有一種情況是因為不可分割的複雜表達式(Un-unparseable Complex Expressions)