在SQL查询的SELECT子句中对Oracle PL / SQL语句进行惰性求值

时间:2009-04-23 16:15:23

标签: sql performance oracle

我在游标中使用的Oracle select语句存在性能问题。在语句中,SELECT子句中的一个术语的评估成本很高(它是一个PL / SQL过程调用,它非常重要地访问数据库)。但是,WHERE子句和ORDER BY子句很简单。

我希望Oracle首先执行WHERE子句来识别与查询匹配的记录集,然后执行ORDER BY子句对它们进行排序,最后评估每个术语中的每个术语。 SELECT条款。当我在游标中使用此语句然后从中拉取结果时,我预计只有在从游标请求每个结果时才会根据需要执行昂贵的SELECT项评估。

但是,我发现这不是Oracle使用的序列。相反,它似乎在执行排序之前为每个匹配WHERE子句的记录评估SELECT子句中的术语。因此,在从游标返回任何结果之前,将为结果集中的每个结果调用调用昂贵的过程。

我希望能够尽快从游标中获取第一个结果。任何人都可以告诉我如何说服Oracle在SELECT语句中评估过程调用,直到执行排序之后?

这可能更容易在示例代码中描述:

给定一个包含exampleabc列的表d,我的声明如下:

select a, b, expensive_procedure(c)
  from example
 where <the_where_clause>
 order by d;

执行此操作时,将为匹配expensive_procedure()子句的每条记录调用WHERE,即使我将该语句作为游标打开并仅从中拉出一个结果。

我尝试将声明重组为:

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吗?

6 个答案:

答案 0 :(得分:2)

为什么在内联视图中将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;

答案 1 :(得分:1)

这是否符合您的意图?

WITH 
cheap AS
(
    SELECT A, B, C
    FROM EXAMPLE
    WHERE <the_where_clause>
)
SELECT A, B, expensive_procedure(C)
FROM cheap
ORDER BY D

答案 2 :(得分:0)

您可能想尝试一下

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;

答案 3 :(得分:0)

这可能是某种形式的工作吗?

FOR R IN (SELECT a,b,c FROM example WHERE ...) LOOP
  e := expensive_procedure(R.c);
  ...
END LOOP;

答案 4 :(得分:0)

如果您的WHERE条件是平等的,i。即

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
        )

,但使用索引会改善查询响应时间(返回第一行的时间)。

答案 5 :(得分:0)

我们尝试过的解决方案的一个关键问题是如何调整生成SQL的应用程序以正确构建查询。构建的SQL将根据检索的列数,where子句中的条件数量和类型以及顺序中的表达式的数量和类型而变化。

返回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,但按其排序至关重要。

我们有理由(我们不会让读者感到厌烦)避免在解决方案中提示,但看起来并非如此。

感谢迄今为止的建议 - 我们已经尝试了大部分建议......