Oracle静态游标的推广
我正在Oracle中创建一个类似OLAP的包,您可以在其中调用一个主控制函数,该函数通过进行大量左连接来组装其返回的输出表。这些联接表在包中的“从属”函数中定义,这些函数使用静态游标返回特定子集,并由函数的参数进行参数化。问题是,这些光标都非常相似 除了生成动态查询并在Oracle静态游标的推广,oracle,cursor,dry,olap,Oracle,Cursor,Dry,Olap,我正在Oracle中创建一个类似OLAP的包,您可以在其中调用一个主控制函数,该函数通过进行大量左连接来组装其返回的输出表。这些联接表在包中的“从属”函数中定义,这些函数使用静态游标返回特定子集,并由函数的参数进行参数化。问题是,这些光标都非常相似 除了生成动态查询并在ref游标中使用它们之外,还有什么方法可以推广这些查询。每次我添加一个函数,我都会有一种奇怪的感觉,作为一个开发人员,这并不是特别优雅 假代码 somePackage function go(param) retur
ref游标
中使用它们之外,还有什么方法可以推广这些查询。每次我添加一个函数,我都会有一种奇怪的感觉,作为一个开发人员,这并不是特别优雅
假代码
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
查询都具有相同的结构:
- 首先,他们以一种有用的方式聚集数据
- 然后,他们根据标准将这些数据分成逻辑部分,然后再次聚合(例如,按部门、按地区等),然后合并结果
- 然后返回结果,转换到相关的
类型中,这样我就可以轻松地进行对象
批量收集
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;
筛选器操作可以帮助使用静态SQL构建动态查询。尤其是当列列表是静态的时 您可能已经考虑过这种方法,但出于性能原因放弃了它。“如果我们只需要结果,为什么要执行每个SQL块 来自其中一个?“你很幸运,优化器已经通过
筛选操作为你做到了这一点
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)
示例查询
首先创建一个函数,每次运行时等待5秒。它将有助于找到执行了哪些查询块
create or replace function slow_function return number is begin
dbms_lock.sleep(5);
return 1;
end;
/
此静态查询由绑定变量控制。有三个查询块,但整个查询运行时间为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以前分析过绑定变量
运行时,甚至不执行不相关的查询块。下面的过滤器操作就是这样做的。(这是一个很糟糕的名字,很多人都这么认为
将所有谓词都称为“filters”。但只有部分谓词会导致FILTER
操作。)
问题
过滤器
操作的文档记录很差。我无法详细解释它何时起作用或不起作用,以及它对查询的其他部分有何影响。例如,在解释计划中,行
的估计值为3,但在运行时Oracle应该能够轻松地估计基数为1。显然,执行计划没有那么动态,基数估计值差可能会导致以后的问题。此外,我还看到一些奇怪的情况,静态表达式没有被适当过滤。但是,如果查询使用简单的相等谓词,就可以了
这种方法允许您删除所有动态SQL,并用大型静态SQL语句替换它。它有一些优势;动态SQL通常是“丑陋”的,并且很难调试。但是,只熟悉过程编程的人倾向于将单个SQL语句视为一个巨大的God函数,这是一种糟糕的做法。他们不会理解,UNION ALL
s创建独立的SQL块
动态SQL可能仍然更好
总的来说,我反对这种做法。你所拥有的是好的,因为它看起来很好。动态SQL的主要问题是人们不把它当作真正的代码;它没有被注释或格式化,最终看起来像一个可怕的混乱,没有人能理解。如果您能够花费额外的时间来生成干净的代码,那么您应该坚持这一点。过滤器操作可以帮助使用静态SQL构建动态查询。尤其是当列列表是静态的时
您可能已经考虑过这种方法,但出于性能原因放弃了它。“如果我们只需要结果,为什么要执行每个SQL块
来自其中一个?“你很幸运,优化器已经通过筛选操作为你做到了这一点
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)
示例查询
首先创建一个函数,每次运行时等待5秒。它将有助于找到执行了哪些查询块
create or replace function slow_function return number is begin
dbms_lock.sleep(5);
return 1;
end;
/
此静态查询由绑定变量控制。有三个查询块,但整个查询运行时间为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以前分析过绑定变量
运行时,甚至不执行不相关的查询块。下面的过滤器操作就是这样做的。(这是一个很糟糕的名字,很多人都这么认为
将所有谓词都称为“filters”。但只有部分谓词会导致FILTER
操作。)
问题
过滤器
操作的文档记录很差。我无法详细解释它何时起作用或不起作用,以及它对查询的其他部分有何影响。例如,在解释计划中,行
的估计值为3,但在运行时Oracle应该能够轻松地估计基数为1。显然,执行计划没有那么动态,基数估计值差可能会导致以后的问题。此外,我还看到一些奇怪的情况,静态表达式没有被适当过滤。但是,如果查询使用简单的相等谓词,就可以了
这种方法允许您删除所有动态SQL,并用大型静态SQL语句替换它。它有一些优势;动态SQL通常是“丑陋”的,并且很难调试。但是,只熟悉过程编程的人倾向于将单个SQL语句视为一个巨大的God函数,这是一种糟糕的做法。他们不会理解,联合所有人都创建独立的