通过使用子查询来限制应用昂贵的过滤器的行来优化 Oracle 查询

问题描述 投票:0回答:1

我有一个 Oracle 查询,它从包含大量联接和过滤器的复杂查询中返回 1 行或 0 行。我的任务是优化该查询,以便它可以更快地执行。

我发现查询性能不佳的一个重要因素是从 WHERE 子句调用函数。简化的查询如下所示:

select id, col1, col2, IS_VALID(id) as VALIDITY_CHECK
FROM table1,
-- a few joins...
WHERE IS_VALID(id) == 'TRUE'
-- AND other filters....

在 WHERE 子句中注释对 IS_VALID 的调用可以大大提高性能。我想我可以理解为什么:在 WHERE 子句中,必须为表中的每一行调用该函数一次,而 SELECT 子句中的函数将为结果集中的每一行调用一次(因此永远不会超过对于一行,即使在 WHERE 子句中注释了函数调用)

为了改进这一点,我正在考虑将查询放在 FROM 子查询中,而不在 WHERE 子句中调用,然后在父查询中对 VALIDITY_CHECK 进行过滤,以便过滤器仅适用于一小部分预过滤的行。看起来像这样:

select * FROM (
  select id, col1, col2, IS_VALID(id) as VALIDITY_CHECK
  FROM table1,
  -- a few joins...
  WHERE -- other filters...
)
WHERE VALIDITY_CHECK='TRUE'

然而,这并没有奏效。事实上,编译器似乎已经优化了我的查询,将所有内容放回单个查询中:当我请求查询计划时,两个查询的计划哈希值完全相同!

我是不是理解错了?我需要做什么才能告诉 Oracle 对第一个查询的结果执行该过滤器?

sql oracle query-optimization
1个回答
0
投票

正如您所发现的,您重写的版本在幕后是相同的。

很可能在查询时对表中的每一行执行该函数的成本太高。但是,您可以选择预先执行该函数一次并存储其结果,这样就不需要在每个查询中重新执行该函数。为此,请尝试创建一个基于函数的索引

create index i_table1_validity on table1 (is_valid(id));

您可能必须使用

is_valid
子句重新编译
deterministic
函数才能创建索引,假设它实际上是确定性的(对于给定输入始终返回相同的值)。

然后就可以简单查询了:

WHERE IS_VALID(id) == 'TRUE'

缺点当然是利用索引中存储的结果,您的查询必须使用索引。如果您要查找表中的大部分行,那么这不是最佳访问路径。全表扫描更好。如果是这种情况,您可能需要通过向表中添加一列并在插入时用

is_valid(id)
填充它来进行非规范化。或者,如果您不能弄乱主表,请使用此信息创建和维护一个 1:1 姐妹表(通过计划刷新或触发器)并在查询中加入它。

最后,考虑是否需要在

is_valid
函数本身内进行调整。可能是您内部存在 SQL 问题,修复该问题将使其足够快,以便在查询时对每一行进行处理是可以接受的。

© www.soinside.com 2019 - 2024. All rights reserved.