SQL 查询的 SELECT 子句中 Oracle PL/SQL 语句的延迟评估
Posted
技术标签:
【中文标题】SQL 查询的 SELECT 子句中 Oracle PL/SQL 语句的延迟评估【英文标题】:Lazy evaluation of Oracle PL/SQL statements in SELECT clauses of SQL queries 【发布时间】:2009-04-23 16:15:23 【问题描述】:我在游标中使用的 Oracle 选择语句存在性能问题。在语句中,SELECT
子句中的一个术语的评估成本很高(它是一个 PL/SQL 过程调用,它对数据库的访问量很大)。但是,WHERE
子句和 ORDER BY
子句很简单。
我希望 Oracle 会首先执行 WHERE
子句来识别与查询匹配的记录集,然后执行 ORDER BY
子句对它们进行排序,最后评估 SELECT
子句中的每个术语.由于我在游标中使用此语句,然后从中提取结果,因此我预计只有在从游标请求每个结果时,才会根据需要执行对 SELECT
术语的昂贵评估。
但是,我发现这不是 Oracle 使用的顺序。相反,它似乎会在执行排序之前评估与 WHERE 子句匹配的每条记录的 SELECT
子句中的术语。因此,在从游标返回任何结果之前,会为结果集中的每个结果结果调用调用代价高昂的过程。
我希望能够尽快从光标中获取第一个结果。谁能告诉我如何说服 Oracle 在执行排序之前不要评估 SELECT 语句中的过程调用?
这在示例代码中可能更容易描述:
给定一个表example
与列a
、b
、c
和d
,我有这样的声明:
select a, b, expensive_procedure(c)
from example
where <the_where_clause>
order by d;
在执行此操作时,会为每条匹配 WHERE
子句的记录调用 expensive_procedure()
,即使我将语句作为游标打开并且只从中提取一个结果。
我尝试将语句重组为:
select a, b, expensive_procedure(c)
from example, (select example2.rowid, ROWNUM
from example example2
where <the_where_clause>
order by d)
where example.rowid = example2.rowid;
ROWNUM
在内部 SELECT
语句中的存在迫使 Oracle 首先评估它。这种重组具有预期的性能优势。不幸的是,它并不总是尊重所需的顺序。
为了清楚起见,我知道我不会改进返回整个结果集所需的时间。我希望缩短从语句返回前几个结果所需的时间。我希望在迭代游标的结果时所花费的时间是渐进的,而不是在返回第一个结果之前所有的时间都过去。
任何 Oracle 专家可以告诉我如何说服 Oracle 在必要之前停止执行 PL/SQL?
【问题讨论】:
您尝试过 FIRST_ROWS(1) 提示吗? 【参考方案1】:为什么要在内联视图中将EXAMPLE 与自身连接起来?为什么不只是:
select /*+ no_merge(v) */ a, b, expensive_procedure(c)
from
( select a, b, c
from example
where <the_where_clause>
order by d
) v;
【讨论】:
【参考方案2】:如果您的WHERE
条件是相等的,即。 e.
WHERE col1 = :value1
AND col2 = :value2
你可以在(col1, col2, d)
上创建一个复合索引:
CREATE INDEX ix_example_col1_col2_d ON example(col1, col2, d)
并提示您的查询使用它:
SELECT /*+ INDEX (e ix_example_col1_col2_d) */
a, b, expensive_procedure(c)
FROM example e
WHERE col1 = :value1
AND col2 = :value2
ORDER BY
d
在下面的示例中,t_even
是一个 1,000,000
行表,索引在 value
上。
从此查询中获取100
列:
SELECT SYS_GUID()
FROM t_even
ORDER BY
value
是即时的(0,03
秒),而这个:
SELECT SYS_GUID()
FROM t_even
ORDER BY
value + 1
大约需要 170
秒来获取第一个 100
行。
SYS_GUID()
在Oracle
中相当昂贵
按照别人的建议,你也可以这样用:
SELECT a, b, expensive_proc(c)
FROM (
SELECT /*+ NO_MERGE */
*
FROM mytable
ORDER BY
d
)
,但使用索引会提高您的查询响应时间(返回第一行的时间)。
【讨论】:
【参考方案3】:这是否符合您的预期?
WITH
cheap AS
(
SELECT A, B, C
FROM EXAMPLE
WHERE <the_where_clause>
)
SELECT A, B, expensive_procedure(C)
FROM cheap
ORDER BY D
【讨论】:
【参考方案4】:你可能想试试这个
select a, b, expensive_procedure(c)
from example, (select /*+ NO_MERGE */
example2.rowid,
ROWNUM
from example example2
where <the_where_clause>
order by d)
where example.rowid = example2.rowid;
【讨论】:
【参考方案5】:可能有某种形式的这种工作?
FOR R IN (SELECT a,b,c FROM example WHERE ...) LOOP
e := expensive_procedure(R.c);
...
END LOOP;
【讨论】:
【参考方案6】:我们尝试过的解决方案的一个关键问题是如何调整生成 SQL 的应用程序以正确构建查询。构建的 SQL 将在检索的列数、where 子句中条件的数量和类型以及 order by 中表达式的数量和类型方面有所不同。
返回 ROWID 以连接到外部的内联视图是我们可以使用的几乎完全通用的解决方案,除非搜索返回了很大一部分数据。在这种情况下,优化器会 [正确地] 确定 HASH 连接比 NESTED LOOP 便宜。
另一个问题是所涉及的一些对象是不能有 ROWID 的 VIEW。
有关信息:“D”不是错字。不选择 order by 的表达式作为返回值的一部分。不是什么不寻常的事情:
select index_name, column_name
from user_ind_columns
where table_name = 'TABLE_OF_INTEREST'
order by index_name, column_position;
在这里,您不需要知道 column_position,但按它排序很关键。
我们有理由(我们不会让读者感到厌烦)避免在解决方案中需要提示,但看起来这是不可能的。
感谢迄今为止的建议 - 我们已经尝试了其中的大部分...
【讨论】:
以上是关于SQL 查询的 SELECT 子句中 Oracle PL/SQL 语句的延迟评估的主要内容,如果未能解决你的问题,请参考以下文章
ORACLE---Unit02: Oracle字符串操作 Oracle数值操作 Oracle日期操作 空值操作