执行计划中是否考虑了功能?

2024-06-26

当查询在 SELECT 或 WHERE 子句中包含 PL/SQL 函数(用户定义函数)时,如何生成执行计划? 它是否也计算这些函数的成本并将其显示在执行计划中,或者这些函数只是被忽略?

在此先感谢您的帮助。


用户生成的函数在 SELECT 或 WHERE 子句中使用时直接贡献很少或没有成本。如果我们希望优化器根据函数的成本做出决策,我们必须手动设置成本相关统计数据 https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/ASSOCIATE-STATISTICS.html#GUID-BD02BA6A-32A7-4093-A6B6-BAE860C0F834命令。

示例架构

对于此示例,创建以下中等大小的表和两个简单​​的函数 - 一个明显快,另一个明显慢。

create table test1 as
select mod(level, 10) a, mod(level, 10) b
from dual
connect by level <= 100000;

begin
    dbms_stats.gather_table_stats(user, 'test1');
end;
/

create or replace function fast_function(p_number number) return number is
begin
    return p_number;
end;
/

create or replace function slow_function(p_number number) return number is
    v_count number;
begin
    select count(*)
    into v_count
    from all_tables;
    return v_count;
end;
/

SELECT 子句中的函数 - 无成本

在 SELECT 子句中调用该函数根本不会改变成本。下面的三个查询选择一个文字、快速函数和慢速函数:

explain plan for select a from test1;
select * from table(dbms_xplan.display);

explain plan for select fast_function(a) from test1;
select * from table(dbms_xplan.display);

explain plan for select slow_function(a) from test1;
select * from table(dbms_xplan.display);

但所有查询都会生成相同的执行计划:

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |   100K|   292K|    47   (3)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST1 |   100K|   292K|    47   (3)| 00:00:01 |
---------------------------------------------------------------------------

WHERE 子句中的函数 - 成本很低

当调用 WHERE 子句中的函数而不是文字时,成本从 48 略微增加到 70。但是快速函数和慢速函数之间没有成本差异。

explain plan for select * from test1 where a = b;
select * from table(dbms_xplan.display);

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       | 10000 | 60000 |    48   (5)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST1 | 10000 | 60000 |    48   (5)| 00:00:01 |
---------------------------------------------------------------------------

explain plan for select * from test1 where fast_function(a) = b;
select * from table(dbms_xplan.display);

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       | 10000 | 60000 |    70  (35)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST1 | 10000 | 60000 |    70  (35)| 00:00:01 |
---------------------------------------------------------------------------

explain plan for select * from test1 where slow_function(a) = b;
select * from table(dbms_xplan.display);

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       | 10000 | 60000 |    70  (35)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST1 | 10000 | 60000 |    70  (35)| 00:00:01 |
---------------------------------------------------------------------------

相关统计数据

我们可以为每次调用该函数设置 cpu_cost、io_cost 和 network_cost。可能有一种方法可以使用跟踪来找到这些特定成本,但成本是一个难以理解的内部幻数,优化器通常只需要一个数量级内的数字来做出正确的决策。我发现慢速函数内查询的总成本为 1000,并将其平均分为 cpu_cost 和 io_cost,如下所示:

associate statistics with functions slow_function default cost(500,500,0);

现在该计划的总成本从 70 急剧增加到 100,000,000:

explain plan for select * from test1 where b = slow_function(b);
select * from table(dbms_xplan.display);

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       | 10000 | 60000 |   100M  (1)| 01:05:07 |
|*  1 |  TABLE ACCESS FULL| TEST1 | 10000 | 60000 |   100M  (1)| 01:05:07 |
---------------------------------------------------------------------------

更重要的是,Oracle 可以使用此成本信息以正确的顺序运行函数。在下面的查询中,Oracle 首先运行快速函数,这几乎不需要任何成本,然后对剩余的行运行慢速函数。

(判断函数执行的顺序有点困难。较低的总体成本意味着函数的运行方式。FILTER 中函数的顺序是另一个标志。在常规 SQL 中,AND 谓词的两侧可以是以任何顺序运行。在解释计划中,执行顺序似乎总是从左到右。)

explain plan for select * from test1 where a = fast_function(a) and b = slow_function(b);
select * from table(dbms_xplan.display);

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |  1000 |  6000 |    10M  (1)| 00:06:31 |
|*  1 |  TABLE ACCESS FULL| TEST1 |  1000 |  6000 |    10M  (1)| 00:06:31 |
---------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("A"="FAST_FUNCTION"("A") AND "B"="SLOW_FUNCTION"("B"))

选择性

尽管名称为“基于成本的优化器”,但我们可能应该更担心基数而不是成本。谓词返回的行数决定了大多数执行计划的选择。 Oracle 对用户定义的函数做出一些默认猜测。例如,在下面的查询中,Oracle 假设该函数仅满足 1% 的行 - 这就是执行计划中的“行”显示 1000 而不是 100000 的原因。

explain plan for select * from test1 where fast_function(a) = 1;
select * from table(dbms_xplan.display);

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |  1000 |  6000 |    70  (35)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST1 |  1000 |  6000 |    70  (35)| 00:00:01 |
---------------------------------------------------------------------------

如果我们知道该函数更具选择性,例如,如果我们知道该函数更有可能只匹配所有行的 0.1%,我们还可以使用ASSOCIATE STATISTICS设置默认选择性。以下命令设置选择性,然后行数从 1000 下降到 100。

associate statistics with functions fast_function default selectivity 0.1;
explain plan for select * from test1 where fast_function(a) = 1;
select * from table(dbms_xplan.display);

---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |   100 |   600 |    70  (35)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST1 |   100 |   600 |    70  (35)| 00:00:01 |
---------------------------------------------------------------------------

在我们的简单计划中,基数并不重要。但在实际查询中,糟糕的基数估计会导致错误决策的连锁反应,从而导致查询速度变慢。帮助优化器做出良好的基数估计通常是性能调优中最重要的部分。

其他类型的函数和统计

这个已经很长的答案仍然只触及函数如何影响执行计划的表面。表函数(返回数据行的函数)完全是另一个主题。我敢打赌,较新的 Oracle 版本中存在动态重新优化功能,在优化器从错误中吸取教训后,这将有助于改进第二次或第三次执行。

我希望我没有阻止您使用自定义函数。绝大多数时候,Oracle会毫不费力地做出正确的决策。如果没有,有一些机制可以帮助纠正这些错误。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

执行计划中是否考虑了功能? 的相关文章

  • COALESCE - 保证短路吗?

    From 这个问题 https stackoverflow com questions 505747 best way to do nested case statement logic in sql server 关于使用 COALESC
  • 如何获取行数据绑定事件中单元格的值?以及如何检查单元格是否为空?

    我正在使用 sqldatasource 和GridView 我想从 GridView 中获取单元格的值RowDataBound事件 因为我不能使用e RowIndex 如果单元格为空 如何检查 updatetng 事件 我用了if null
  • 合并和替换函数

    我试图用 替换查询中的所有空值 因为我们的接口不接受 NULL 作为条目 我遇到了一段代码 其中我使用 REPLACE 函数删除 SSN 中的 DASHES 我如何告诉系统为 SSN 列中的 NULL 值提供 并且仍然对非空条目 SSN 执
  • ORACLE 更新并返回 OLD 和 NEW 值

    PL SQL Oracle 12c 中是否有与此 T SQL 查询等效的内容 UPDATE A SET A columnA 10 WHERE A columnB lt 30 OUTPUT INSERTED DELETED 查询更新表A 同时
  • 有没有办法刷新 Oracle 中 PL/SQL 的输出?

    我有一个从 shell 脚本中调用的 SQL 脚本 需要很长时间才能运行 目前它包含dbms output put line不同点的声明 这些打印语句的输出会出现在日志文件中 但仅在脚本完成后才会出现 有什么方法可以确保脚本运行时输出出现在
  • 使用 JavaScript 清理 SQL 数据

    我有一堆具有各种输入元素的表单 我想在服务器端清理这些 yes 我使用服务器端 JavaScript 将这些输入用作参数 并防止特殊字符格式错误 在你走之前 比如 这不属于 JavaScript 的领域 等等 我正在使用一个价值数百万美元的
  • SQL Server 2008:TOP 10 和不同的一起

    正如标题所示 我正在使用 SQL Server 2008 如果这个问题非常基本 我深表歉意 我才使用 SQL 几天 现在我有以下查询 SELECT TOP 10 p id pl nm pl val pl txt val from dm la
  • 主键和代理键有什么区别?

    我用谷歌搜索了很多 但没有找到带有示例的确切直接答案 任何例子都会更有帮助 主键是表中的唯一键 您选择它可以最好地唯一标识表中的记录 所有表都应该有一个主键 因为如果您需要更新或删除一条记录 您需要知道如何唯一标识它 代理键是人工生成的键
  • 具有多个表的 SQL select 语句

    给出以下两个表 Person table id pk first middle last age Address table id pk person id fk person id street city state zip 如何创建返回
  • 当用户单击链接时如何在表中创建新字段

    我的表格如下图所示 In order to insert data from this form into table I coded this supplier info supplier name POST supplier name
  • 从 SQL 数据库反序列化数据

    我有一个小应用程序 由数据库支持 SQLite 但它与问题并不真正相关 我定义了一些类型 例如 data Whatever Whatever Int Int String String data ImportantStuff Importa
  • 在Django中通过ManyToMany关系添加一个对象

    Django 的 ManyToMany 字段 https docs djangoproject com en dev ref models fields django db models ManyToManyField可以使用填充my fi
  • INSERT INTO 存储过程的输出

    我正在编写一个存储过程 首先在表中插入一个新行 然后 另一个查询需要此查询生成的 ID 是否可以使用 OUTPUT 访问预先生成的 ID 这就是我到目前为止所做的 这几乎是一个猜测 但没有成功 ALTER PROCEDURE dbo add
  • End using 是否关闭打开的 SQL 连接

    如果我将 SQLConnection 包装在 using 中 我应该关闭它还是最终 using 处理它 using cn as new system data sqlclient sqlconnection cn open do a bun
  • 如何在Oracle中获取每周数据

    我制作了一个矩阵报告 其中需要根据选择参数动态显示列 我有一个日期选择参数 如果我在选择参数上选择日期为 03 01 2010 2010 年 3 月 1 日 那么它应该显示为 3 月 1 日 3 月 7 日 这取决于你追求什么 如果您在接下
  • 限制 SQL 查询的响应时间

    我在这里发布了一个关于我的 gridview 绑定的问题 将GridView与多条记录绑定 https stackoverflow com questions 5599704 bind gridview with many records
  • 使用 cx_oracle 返回 MERGE 中受影响的行数

    如何在 CX Oracle 中执行 MERGE INTO sql 命令来获取受影响的行数 当我在cx oracle 上执行MERGE SQL 时 我得到的cursor rowcount 为 1 有没有办法获取受合并影响的行数 由于 cx o
  • 原则 2:级联持久 Oracle“IDENTITY”返回 0 作为最后插入的 ID

    我在 oracle 中使用原则 2 数据库中的表有一些生成 ID 的触发器 我的表的 ID 映射如下所示 orm Id orm Column type integer orm GeneratedValue strategy IDENTITY
  • Oracle TO_DATE 函数中跳过字符

    我正在导入 tsv 中具有 SQL Server 格式日期的数据 yyyy mm dd hh24 mi ss mmm 使用 SQL Developer 的导入数据向导导入 Oracle 数据库 我怎样才能忽略 mmm用于将它们导入 DATE
  • 如何使用 SQL 计算一条路线的行驶次数?

    我需要确定在给定的日期范围内每辆车行驶特定路线的次数 但建立在数据库之上的 GPS 管理软件没有此功能 该数据库包含多个存储 GPS 路线和位置数据的表 路线由多个位置和序列号组成 位置是附加到名称的一组上限和下限纬度 经度值 车辆每分钟将

随机推荐

  • 删除重复字符

    我如何删除重复字符 例如删除字母k in cakkkke让它成为cake 执行此操作的一种简单方法是循环遍历字符串的每个字符 如果该字符不是前一个字符的重复 则将字符串的每个字符附加到新字符串 下面是一些可以执行此操作的代码 newStri
  • 为什么java字符串在MYSQL中不保存为UTF-8?

    message new String round id getBytes UTF 8 conn DriverManager getConnection jdbc mysql host db useUnicode true character
  • 使用 Swift 创建随机 CGPoint

    所以 我正在尝试开发一个用 Swift 编写的简单游戏 但我在做一件非常简单的事情时遇到了困难 我无法创建随机 CGPoint 使用 arc4random 时 会出现编译器错误 告诉我无法在 CGPoint 中使用 Int32 那么 有什么
  • 在 Python 中将 int 转换为字符串

    我希望能够生成多个名为 fileX txt 的文本文件 其中 X 是某个整数 for i in range key filename ME i txt Error here Can t concat a string and int fil
  • 使用 MVC 5 RouteArea 属性时找不到默认区域视图

    我有一个包含多个区域的 MVC5 项目 我有一个默认区域 名为Default 并在其中有一个默认控制器 名为DefaultController 这可以通过站点路径访问 RouteArea public class DefaultContro
  • Python:按组计算数据框中的特定出现次数

    假设我有一个 df df pd DataFrame id 12 35 37 67 99 78 product banana apple banana pear banana apple reordered 1 0 0 1 1 1 id pr
  • 使用指针接收器调用函数的 Go 语法

    在Go中 如果我定义一个带有指针的函数作为接收者 它是否应该只允许从指针调用该函数 为什么从值本身调用这个函数就可以 并且有同样的效果 例如 在以下程序中 m1 reset 和 m2 reset 具有相同的效果 即使 m1 是一个值而 m2
  • FilesystemIterator 中的顺序

    http php net manual en class filesystemiterator php http php net manual en class filesystemiterator php 我注意到FilesystemIt
  • SQL:从单个查询列出多对多

    我有 3 个表 分别代表 Users Roles 和多对多 UsersInRoles 键为 UserId RoleId 相关列 用户名 角色名 在管理 html 应用程序中 我想显示所有用户及其所在角色的列表 我尝试从 SQL 构建一个将返
  • REST API 或远程 Celery/Django 工作人员的“直接”数据库访问?

    我正在开发一个项目 该项目将在美国不同地点的机器上设置多个芹菜工人 这些机器将通过互联网进行通信 我是否最好将我的 Django 项目分发到每台计算机并使用数据库主机的数据库凭据配置它们 或者我应该有一个 主 Django 数据库主机 为远
  • 带有 self 实例的 Django 模型方法

    我试图将一些功能逻辑转移到模型的方法中 而不是视图中 我认为它属于 class Spans models Model snow models IntegerField wind models IntegerField exposure mo
  • 雅虎财经 API 随机引用错误日期 - 从今天到昨天几天前

    我正在使用雅虎财经 API 从已关闭的市场获取报价 我通常在晚上 11 点 美国时间 左右运行 API API有时会返回今天的市场数据 有时会返回昨天的市场数据 它似乎是随机的 如果您连续运行几次 您可以看到它在这些日期之间随机切换 我怎样
  • scipy 稀疏矩阵的元素级 exp()

    我有一个很大稀疏csc matrix x 我想对其进行元素 exp 基本上我想要的是得到与我得到的结果相同的结果numpy exp x toarray 但我不能这样做 我的记忆不允许我将稀疏矩阵转换为数组 还有出路吗 提前致谢 如果你没有记
  • 禁用 WireMock 的日志记录

    我在用着 AutoConfigureWireMock port 0 初始化模拟服务器 这是用 Kotlin 编写的设置类 AutoConfigureWireMock port 0 SpringBootTest webEnvironment
  • 在 Rails 中从注册表单创建 Devise 用户时如何创建另一个对象?

    我的系统中有不同类型的用户 比方说 其中一种是设计师 class Designer lt ActiveRecord Base attr accessible user id portfolio id some designer specif
  • 存储过程 EXEC 与 sp_executesql 的区别?

    我写了两个存储过程 其中一个是sp executesql而其他没有 sp executesql 两者都正确执行相同的结果 我不明白两者之间有什么区别 EXEC SQL 与 EXEC sp executesql SQL N eStatus v
  • 多线程归并排序,添加额外的线程

    我在java中的多线程合并排序算法中面临一个问题 我应该将代码修改为 3 4 5 6 7 8 线程合并排序 将原始数组划分为subArrays 目前它有2subArrays 如何将原始数组拆分为 3 4 5 6 7 8subArray是为了
  • orderBy 随递减排序和递增排序的变化

    是否有一种标准方法可以按几列对 data frame 进行排序 但会发生减少或增加的变化 例如 您可能希望按一个变量 递减 和下一个变量 递增 对 data frame 进行排序 有没有类似的东西 mydf order mydf myvar
  • 使用 max_align_t 存储一大块字节

    In 这个线程 https stackoverflow com q 55867320 1606345我被建议使用max align t为了获得针对任何类型正确对齐的地址 我最终创建了动态数组的实现 include
  • 执行计划中是否考虑了功能?

    当查询在 SELECT 或 WHERE 子句中包含 PL SQL 函数 用户定义函数 时 如何生成执行计划 它是否也计算这些函数的成本并将其显示在执行计划中 或者这些函数只是被忽略 在此先感谢您的帮助 用户生成的函数在 SELECT 或 W