ORACLE数据库中执行计划出现INTERNAL_FUNCTION一定是隐式转换吗?
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),下面通过一个例子来说明一下
SQL> drop table t purge;
Table dropped.
SQL> CREATE TABLE t AS SELECT * FROM dba_objects;
Table created.
SQL> SELECT COUNT(*) FROM t WHERE owner = 'SYS' OR owner = 'SYSTEM';
COUNT(*)
----------
23851
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID 77xzyugx5q3kf, child number 0
-------------------------------------
SELECT COUNT(*) FROM t WHERE owner = 'SYS' OR owner = 'SYSTEM'
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 108 (100)| |
| 1 | SORT AGGREGATE | | 1 | 17 | | |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
|* 2 | TABLE ACCESS FULL| T | 22494 | 373K| 108 (7)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("OWNER"='SYS' OR "OWNER"='SYSTEM'))
Note
-----
- dynamic sampling used for this statement
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
现在,我们让谓词稍微复杂一点,在查询条件中添加另一个OR,但这是针对另一列object_id的查询条件,如下所示:
SQL> SELECT COUNT(*) FROM t WHERE owner = 'SYS' OR owner = 'SYSTEM' OR object_id = 123;
COUNT(*)
----------
23851
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID 9vh8b6ku8sd1t, child number 0
-------------------------------------
SELECT COUNT(*) FROM t WHERE owner = 'SYS' OR owner = 'SYSTEM' OR
object_id = 123
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 111 (100)| |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| 1 | SORT AGGREGATE | | 1 | 30 | | |
|* 2 | TABLE ACCESS FULL| T | 22494 | 659K| 111 (10)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter((INTERNAL_FUNCTION("OWNER") OR "OBJECT_ID"=123))
Note
-----
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
- dynamic sampling used for this statement
24 rows selected.
修改WHERE查询条件后,OWNER表上的两个查询条件消失了,由INTERNAL_FUNCTION替换了,接下来,让我们用IN运算符,而不是OR,但是上面SQL是不同字段之间的OR,我们需要修改一下SQL语句
SQL> SELECT COUNT(*) FROM t WHERE owner IN ('SYS','SYSTEM','SCOTT') AND object_type = 'TABLE';
COUNT(*)
----------
896
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID gcqgrmtna9g1u, child number 0
-------------------------------------
SELECT COUNT(*) FROM t WHERE owner IN ('SYS','SYSTEM','SCOTT') AND
object_type = 'TABLE'
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 111 (100)| |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| 1 | SORT AGGREGATE | | 1 | 16 | | |
|* 2 | TABLE ACCESS FULL| T | 894 | 14304 | 111 (10)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("OBJECT_TYPE"='TABLE' AND INTERNAL_FUNCTION("OWNER")))
20 rows selected.
很不幸,上面执行计划中谓词部分依然出现了INTERNAL_FUNCTION,我们在逻辑上简化一下,只搜寻同一个字段上的三个值:
SQL> SELECT COUNT(*) FROM t WHERE owner IN ('SYS','SYSTEM','SCOTT');
COUNT(*)
----------
23857
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID 2qazbqj67y17s, child number 0
-------------------------------------
SELECT COUNT(*) FROM t WHERE owner IN ('SYS','SYSTEM','SCOTT')
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 111 (100)| |
| 1 | SORT AGGREGATE | | 1 | 7 | | |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
|* 2 | TABLE ACCESS FULL| T | 24133 | 164K| 111 (10)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("OWNER"='SCOTT' OR "OWNER"='SYS' OR "OWNER"='SYSTEM'))
19 rows selected.
如上所示,它确实生效了,ORACLE已将IN谓词转换为(或至少在执行计划中显示了)了一堆OR-ed条件(针对同一列)
你可能已经看到了前面的例子的执行计划输出内容– DBMS_XPLAN.DISPLAY_CURSOR无法解释在单个执行计划步骤中应用的“复杂”的复合谓词,其中包括多个不同的列,并且至少其中一个列具有多个要检查的值(例如列表中或OR-ed谓词)
DISPLAY_CURSOR从何处获取数据并进行解释呢?
DBMS_XPLAN.DISPLAY_CURSOR从V$SQL_PLAN获取其执行计划的相关数据,谓词部分来自ACCESS_PREDICATES和FILTER_PREDICATES列。但是当我直接查询V$SQL_PLAN时,我仍然看到相同的问题:
SQL> SELECT id, filter_predicates FROM v$sql_plan WHERE sql_id = 'gcqgrmtna9g1u';
ID FILTER_PREDICATES
---------- ------------------------------------------------------------
0
1
2 (INTERNAL_FUNCTION("OWNER") AND "OBJECT_TYPE"='TABLE')
你可能已经注意到,上面的原始ORed条件周围也有括号(),这在9i中,意味着谓词周围的“二进制”执行计划中存在“无法解释的”内部函数,但是在这种情况下(如10g +支持internal_function命名),不应出现空白的函数名称……不确定为什么会出现这种情况,但这对本篇文章来说太深入了。
V$SQL_PLAN视图本身访问库高速缓存(library cache)中的实际“二进制”子游标(在使用了适当的latches/pins/mutexe之后)并对其进行解析。为什么用这样的术语-其实并不是根据人类容易理解的输入并将其转换为计算机可理解的“二进制”格式。悄悄相反– V$SQL_PLAN访问游标中的“二进制”执行计划的内存结构,并将其转换为人类可读的执行计划输出。甚至还有一个参数控制此V$SQL_PLAN的行为,如果将其设置为false,则ACCESS_PREDICATES和FILTER_PREDICATES列将为空:
这段真不好翻译(有可能翻译不当),参考英文原文如下:
The V$SQL_PLAN view itself accesses the actual “binary” child cursor in library cache (after taking appropriate latches/pins/mutexes) and UNPARSES it. Why such term – well isn’t parsing something that takes a human readable input and translates it into computer-understandable “binary” format. Thus unparsing is the opposite – V$SQL_PLAN accesses the cursor’s “binary” execution plan memory structure and translates it to human-readable execution plan output. There’s even a parameter controlling this V$SQL_PLAN behavior, if it’s set to false, the ACCESS_PREDICATES and FILTER_PREDICATES columns will be empty there:
SQL> @pd unparse
Show all parameters and session values from x$ksppi/x$ksppcv...
NAME VALUE DESCRIPTION
----------------------------- --------- -----------------------------------------------
_cursor_plan_unparse_enabled TRUE enables/disables using unparse to build
projection/predicates
顺便说一句,为什么我总是说“二进制”执行计划并用双引号括起来? 这是因为我想强调,ORACLE的实际执行计划并不像我们在屏幕上看到的输出的文本那样,这些输出的“执行计划”只是为了在troubleshooting的时候,更好的适应人类的阅读习惯而生成的文本(这里其实就是说转换成了符合人类阅读系统的文本),执行计划也不是真正的可执行二进制文件(如oracle.exe中一样),也没有直接反馈给CPU执行。 库缓存子游标中的物理执行计划(physical execution plan)是一堆操作码(a bunch of opcodes),object_id和指针,用于定义行源执行的层次结构和顺序。 SQL执行引擎去循环遍历这些操作码,对其进行解码,然后知道下一步该做什么(要调用哪个rowsource函数)。
因此,如上所述,某些具有复杂AND / OR条件的谓词被DBMS_XPLAN显示为INTERNAL_FUNCTION()。DISPLAY_CURSOR和V$SQL_PLAN因为它们也无法完全解码(解析)执行计划信息。
Using the good old EXPLAIN PLAN
不过有个好消息! 旧的EXPLAIN PLAN命令能够正确的解析这些复杂谓词(当然仅仅是其中一部分),当EXPLAIN PLAN以一种特殊、更加仪器化的方式(more instrumented way)解析给定的SQL语句时,它显然手头有更多信息(并且它还使用了更多的内存)。或者可能只是谁写了V$SQL_PLAN,没有编写一段代码来解析更复杂的谓词:),如下所示:
SQL> EXPLAIN PLAN FOR
2 SELECT COUNT(*) FROM t WHERE owner IN ('SYS','SYSTEM','SCOTT') AND object_type = 'TABLE';
Explained.
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2966233522
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 16 | 111 (10)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 16 | | |
|* 2 | TABLE ACCESS FULL| T | 894 | 14304 | 111 (10)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
---------------------------------------------------
2 - filter("OBJECT_TYPE"='TABLE' AND ("OWNER"='SCOTT' OR
"OWNER"='SYS' OR "OWNER"='SYSTEM'))
15 rows selected.
SQL>
这真是一个奇迹,INTERNAL_FUNCTION消失不见了,所有的谓词都正确的显示了,EXPLAIN PLAN命令在这里非常有用。
因此,尽管我通常不使用EXPLAIN PLAN命令,因为EXPLAIN PLAN输出的执行计划可能会骗你,但是,每当我在DISPLAY_CURSOR/V$SQL_PLAN/SQL Monitor输出中看到INTERNAL_FUNCTION时,我都会运行EXPLAIN PLAN命令执行同一个SQL,希望快速找出其中的谓词INTERNAL_FUNCTION代表的真正意义。
参考资料:
https://blog.tanelpoder.com/2013/01/16/what-the-heck-is-the-internal_function-in-execution-plan-predicate-section/
https://docs.oracle.com/cd/E11882_01/server.112/e25523/part_avail.htm#sthref141
ORACLE数据库中执行计划出现INTERNAL_FUNCTION一定是隐式转换吗?的更多相关文章
- 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则未经解析。
SELECT CONVERT(VARCHAR(100), 列名) FROM Table 提示错误: 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则 ...
- Oracle数据库查看执行计划
基于ORACLE的应用系统很多性能问题,是由应用系统SQL性能低劣引起的,所以,SQL的性能优化很重要,分析与优化SQL的性能我们一般通过查看该SQL的执行计划,本文就如何看懂执行计划,以及如何通过分 ...
- ORACLE数据库查看执行计划的方法
一.什么是执行计划(explain plan) 执行计划:一条查询语句在ORACLE中的执行过程或访问路径的描述. 二.如何查看执行计划 1: 在PL/SQL下按F5查看执行计划.第三方工具toad等 ...
- c++中istream类型到bool类型的隐式转换
事情的起因是见到了这种用法: while(cin>>m>>n&&m&&n) { } 现在分析一下,cin>>m>>n返回 ...
- 基于Oracle的SQL优化(崔华著)-整理笔记-第2章“Oracle里的执行计划”
详细介绍了Oracle数据里与执行计划有关的各个方面的内容,包括执行计划的含义,加何查看执行计划,如何得到目标SQL真实的执行计划,如何查看执行计划的执行顺序,Oracle数据库里各种常见的执行计划的 ...
- Oracle性能优化之Oracle里的执行计划
一.执行计划 执行计划是目标SQL在oracle数据库中具体的执行步骤,oracle用来执行目标SQL语句的具体执行步骤的组合被称为执行计划. 二.如何查看oracle数据库的执行计划 oracle数 ...
- Oracle数据库中直方图对执行计划的影响
在Oracle数据库中,CBO会默认目标列的数据在其最小值low_value和最大值high_value之间均匀分布,并按照均匀分布原则,来计算目标列 施加查询条件后的可选择率以及结果集的cardin ...
- 查看Oracle数据库中的执行计划
1.set autotrace traceonly命令 2.explain plan for命令 1)explain plan for select * from dual; 2)select * f ...
- 如何在Oracle数据库中查看哪些用户在执行哪些SQL
对于DBA来说,这是一个非常常见的问题,DBA需要找出以下问题: 1.哪些用户在跑哪些SQL? 2.一个特定的SQL是被哪个用户在执行? 3.一个特定的用户在跑哪些SQL? 从这些问题中可以很明显的看 ...
随机推荐
- docker概念
一.docket概述 什么是docker? 为什么docker会出现 一款产品从开发到上线,从操作系统,到运行环境,再到应用配置.作为开发+运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不 ...
- 09-kubernetes configMap secret
目录 配置容器化应用配置的方式 命令创建和测试configMap 创建一个Pod 挂在测试 命令行文件类创建方式 创建Pod测试 创建后测试 贴近实际进行测试 创建后测试 secret 举例测试 ge ...
- 2019-2020-2 20199317《Linux内核原理与分析》第二周作业
第一章 计算机工作原理 1 存储程序计算机工作模型 存储程序计算机的主要思想是将程序存放在计算机存储器中,然后按存储器中的存储程序的首地址执行程序的第一条指令,以后就按照该程序中编写 ...
- 形式语言与自动机|DFA识别句子
实验二 DFA识别句子 一.实验目的 加深对DFA工作原理的理解. 二.实验内容 1.设计固定DFA.也就是说用if-then-else(一般用来实现字母表中只有两个字母的情况).switch(大于两 ...
- 与karma、angular的第一次亲密接触
首先要了解什么是karma,karma干嘛用的,它的好朋友jasmine又是啥?这些文章可以帮助你: karma干嘛的? angular与karma1 angular与karma2 看了以上几篇文章之 ...
- Semaphore回顾
用途 在多线程访问可变变量时,是非线程安全的.可能导致程序崩溃.此时,可以通过使用信号量(semaphore)技术,保证多线程处理某段代码时,后面线程等待前面线程执行,保证了多线程的安全性.使用方法记 ...
- 机器学习笔记(六) ---- 支持向量机(SVM)
支持向量机(SVM)可以说是一个完全由数学理论和公式进行应用的一种机器学习算法,在小批量数据分类上准确度高.性能好,在二分类问题上有广泛的应用. 同样是二分类算法,支持向量机和逻辑回归有很多相似性,都 ...
- 转:Java transient关键字使用小记
哎,虽然自己最熟的是Java,但很多Java基础知识都不知道,比如transient关键字以前都没用到过,所以不知道它的作用是什么,今天做笔试题时发现有一题是关于这个的,于是花个时间整理下transi ...
- luogu P4302 [SCOI2003]字符串折叠
题目描述 折叠的定义如下: 一个字符串可以看成它自身的折叠.记作S = S X(S)是X(X>1)个S连接在一起的串的折叠.记作X(S) = SSSS-S(X个S). 如果A = A', B = ...
- linux命令之head、tail命令详解
head 语法 例子 tail 语法 例子 head和tail组合用法举例 head 语法 head [-n -k ]... [FILE]... 例子 默认是显示开头前10行. head /etc/p ...