【问题标题】:Generalising Oracle Static Cursors泛化 Oracle 静态游标
【发布时间】:2012-08-23 11:59:22
【问题描述】:

我正在 Oracle 中创建一个类似 OLAP 的包,您可以在其中调用一个主控制函数,该函数通过进行大量左连接来组装其返回的输出表。这些连接表在包内的“从”函数中定义,这些函数使用静态游标返回特定子集,由函数的参数进行参数化。问题是,这些光标都非常相似。

除了生成动态查询并在ref cursor 中使用它们之外,还有什么方法可以概括这些。每次我添加一个函数时,我都会有一种奇怪的感觉,作为一个开发者,这不是特别优雅!


伪代码

somePackage
  function go(param)
    return select    myRows.id,
                     stats1.value,
                     stats2.value
           from      myRows
           left join table(somePackage.stats1(param)) stats1
           on        stats1.id = myRows.id
           left join table(somePackage.stats2(param)) stats2
           on        stats2.id = myRows.id

  function stats1(param)
    return [RESULTS OF SOME QUERY]

  function stats2(param)
    return [RESULTS OF A RELATED QUERY]

stats 查询都具有相同的结构:

  • 首先他们以有用的方式聚合数据
  • 然后他们根据标准将这些数据分成逻辑部分,并再次聚合(例如,按部门、按地区等),然后合并结果
  • 然后他们返回结果,转换成相关的object类型,这样我就可以轻松地做一个bulk collect

类似:

cursor myCursor is
  with fullData as (
    [AGGREGATE DATA]
  ),
  fullStats as (
    [AGGREGATE FULLDATA BY TOWN]
    union all
    [AGGREGATE FULLDATA BY REGION]
    union all
    [AGGREGATE FULLDATA BY COUNTRY]
  )
  select myObjectType(fullStats.*)
  from   fullStats;

...

open myCursor;
fetch myCursor bulk collect into output limit 1000;
close myCursor;

return output;

【问题讨论】:

  • 您的问题是“如何在不生成动态查询的情况下生成动态查询?”
  • 点了!我的理由是我所有的查询都具有相同的结构:所以也许更准确的总结是“我可以创建多态游标吗?”

标签: oracle cursor dry olap


【解决方案1】:

过滤操作有助于使用静态 SQL 构建动态查询。尤其是当列列表是静态的时。

您可能已经考虑过这种方法,但出于性能原因放弃了它。 “如果我们只需要结果,为什么要执行每个 SQL 块 来自其中一个?”你很幸运,优化器已经通过 FILTER 操作为你完成了这项工作。

示例查询

首先创建一个每次运行等待 5 秒的函数。它将帮助查找执行了哪些查询块。

create or replace function slow_function return number is begin
    dbms_lock.sleep(5);
    return 1;
end;
/

此静态查询由绑定变量控制。有 3 个查询块,但整个查询运行时间为 5 秒而不是 15 秒。

declare
  v_sum number;
  v_query1 number := 1;
  v_query2 number := 0;
  v_query3 number := 0;
begin
  select sum(total)
  into v_sum
  from
  (
    select total from (select slow_function() total from dual) where v_query1 = 1
    union all
    select total from (select slow_function() total from dual) where v_query2 = 1
    union all
    select total from (select slow_function() total from dual) where v_query3 = 1
  );
end;
/

执行计划

这种表现不是好运的结果;这不仅仅是 Oracle 在另一个谓词之前随机执行一个谓词。 Oracle 之前分析绑定变量 运行时,甚至不执行不相关的查询块。这就是下面的FILTER 操作正在做的事情。 (这是一个不好的名字,很多人普遍 将所有谓词称为“过滤器”。但只有其中一些会导致FILTER 操作。)

select * from table(dbms_xplan.display_cursor(sql_id => '0cfqc6a70kzmt'));

SQL_ID  0cfqc6a70kzmt, child number 0
-------------------------------------
SELECT SUM(TOTAL) FROM ( SELECT TOTAL FROM (SELECT SLOW_FUNCTION() 
TOTAL FROM DUAL) WHERE :B1 = 1 UNION ALL SELECT TOTAL FROM (SELECT 
SLOW_FUNCTION() TOTAL FROM DUAL) WHERE :B2 = 1 UNION ALL SELECT TOTAL 
FROM (SELECT SLOW_FUNCTION() TOTAL FROM DUAL) WHERE :B3 = 1 )

Plan hash value: 926033116

-------------------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT |      |       |       |     6 (100)|          |
|   1 |  SORT AGGREGATE  |      |     1 |    13 |            |          |
|   2 |   VIEW           |      |     3 |    39 |     6   (0)| 00:00:01 |
|   3 |    UNION-ALL     |      |       |       |            |          |
|*  4 |     FILTER       |      |       |       |            |          |
|   5 |      FAST DUAL   |      |     1 |       |     2   (0)| 00:00:01 |
|*  6 |     FILTER       |      |       |       |            |          |
|   7 |      FAST DUAL   |      |     1 |       |     2   (0)| 00:00:01 |
|*  8 |     FILTER       |      |       |       |            |          |
|   9 |      FAST DUAL   |      |     1 |       |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - filter(:B1=1)
   6 - filter(:B2=1)
   8 - filter(:B3=1)

问题

FILTER 操作记录不充分。我无法详细解释它何时起作用或不起作用,以及它对查询的其他部分的确切影响。例如,在解释计划中,Rows 估计为 3,但在运行时 Oracle 应该能够轻松地估计基数为 1。显然执行计划不是那么动态的,糟糕的基数估计可能会导致以后的问题。此外,我还看到了一些奇怪的情况,静态表达式没有被适当地过滤。但是如果查询使用简单的等式谓词应该没问题。

这种方法允许您删除所有动态 SQL 并将其替换为大型静态 SQL 语句。有一些优点;动态 SQL 通常“丑陋”且难以调试。但是只熟悉过程式编程的人倾向于将单个 SQL 语句视为一个巨大的上帝函数,这是一种不好的做法。他们不会欣赏UNION ALLs 创建独立的 SQL 块

动态 SQL 可能仍然更好

一般来说,我会推荐反对这种方法。你拥有的东西很好,因为它看起来很好。动态 SQL 的主要问题是人们不把它当作真正的代码。它没有评论或格式化,最终看起来像一个没人能理解的可怕混乱。如果您能够花费额外的时间来生成干净的代码,那么您应该坚持下去。

【讨论】:

  • 有趣 :) 老实说,这个问题出现已经很久了,我什至不确定我采用了什么解决方案。我认为我坚持使用类似的功能并使用代码重复,而不是动态 SQL 生成,因为它更快(即优化了静态游标)。然而,了解优化器有什么技巧总是很有趣的!谢谢:)
猜你喜欢
  • 2014-11-20
  • 2020-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多