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 与列abcd,我有这样的声明:

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日期操作 空值操作

Oracle 到 SQL Server:月 + 年 where 子句

SQL Server 之 子查询与嵌套查询

Oracle SQL where 子句中的函数

Oracle数据库--过滤和排序

oracle的查询数据(检索数据)