我可以依靠在SQL中首先执行的函数吗


9

请考虑以下脚本:

create or replace function f(p_limit in integer) return integer as
begin
  set_global_context ('limit', p_limit);
  return p_limit;
end;
/

create view v as 
select level as val from dual connect by level<=sys_context('global_context','limit');

select f(2), v.* from v;

/*
F(2)                   VAL                    
---------------------- ---------------------- 
2                      1                      
2                      2                      
*/

select f(4), v.* from v;

/*
F(4)                   VAL                    
---------------------- ---------------------- 
4                      1                      
4                      2                      
4                      3                      
4                      4                      
*/

f(x)是否可以依赖于在视图中读取上下文之前执行代码,就像在本测试用例中在10.2上运行它一样?


不禁想起登录触发器可能更合适(如果该水平将始终是相同的,那就是)
Philᵀᴹ

@Phil这只是一个例子-我正在使用sys_context参数化视图,并且参数每次都不同。如果您知道一种从SQL设置全局上下文而又不会弄乱这种方法的方法,那么我也很想听听!
杰克说试试topanswers.xyz 2012年

1
@JackDouglas:参数化视图的想法对我而言并不“正确”。在MSSQL下,您可以尝试使用返回结果集(而不是值)的用户定义函数来完成操作-然后SELECT stuff FROM dbo.FuncReturningTable(param)可以执行类似操作。Oracle可能具有等效的功能。尽管如果在大型数据集上使用此方法,则要小心监视性能:我不确定查询计划程序需要多大的亮度才能根据此类语法制定有效的计划。
David Spillett

@David参数化视图通常是通过sys_context完成的-只是通常情况下,您需要在执行查询之前设置上下文(例如,使用一些PL / SQL)。Oracle具有设置返回和/或流水线功能,但它们不是实现此功能的“正常”方式。明确地说,我认为标题中的问题的答案是“否”-我只是想知道是否有人更了解。
杰克说试试topanswers.xyz 2012年

Answers:


8

没有。

如果使用针对where子句的上下文过滤(而不是connect by)重写视图,则将获得上下文的先前设置的值:

create table t as 
 select rownum r from dual connect by level <= 10;

create or replace view v as 
  select r val from t where r <=sys_context('global_context','limit');

select f(2), v.* from v;

F(2) VAL
---- ---
   2   1 
   2   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 

select f(4), v.* from v;

F(4) VAL
---- ---
   4   1 
   4   2 
   4   3 
   4   4 

由于where子句是在选择列之前进行评估的,因此直到读取上下文之后才设置传递给函数的值。sys_context调用在查询中的位置(选择,位置,分组依据等)将完全影响设置此值的时间。


+1,在我的书中几乎“结案”了,谢谢。
杰克说试试topanswers.xyz 2012年

2

一般来说,在评估单个SQL语句时,您不能安全地假设任何有关DBMS处理事情的顺序。这就是为什么许多DBMS不允许使用这种功能产生副作用的原因(即MSSQL不允许函数设置全局/连接状态(在此执行操作或更改表内容))。一系列语句必须以一种有意义的方式执行,即从一个步骤到下一个步骤(即,它们是串行运行的,或者以您无法确定它们不是的方式运行),但是在单个语句中,查询计划器具有自由统治权,只要它不会在不存在的地方引入歧义即可(在您的示例中,歧义已经存在,因为该函数具有影响视图的副作用)。

如果查询计划程序足够聪明,可以检测到该视图受该函数的副作用影响,那么如果您加入另一个可能使用不同输入值调用该函数的视图,该怎么办?它很快就会变得很毛茸茸-这种事情就是为什么通常在任何编程上下文中,函数都不应产生超出其自身输出的影响的原因。

在这个特定的例子中,我想说f(x)不太可能首先被调用,因为它是语句的“显示”部分:从视图中返回的结果集很可能在函数中的任何函数之前被检索。评估要返回的列列表。当然,这取决于所使用的DBMS会有所不同:我不是Oracle专家,您的测试结果表明,在这些实例中确实确实首先调用了该函数。但是我会警惕完全依赖任何一条SQL语句中的执行顺序-即使它始终按您现在期望的方式运行,在将来的修订版中可能也不会这样做(除非正式记录了执行将永远进行的某个地方)这样)。


2
好的答案,但是我觉得杰克正在寻找一种确定的Oracle技术答案。
菲尔(Philᵀᴹ)

1

该文档仅保证“优化程序首先尽可能全面地评估包含常量的表达式和条件”。(10.211.2)。您不能保证它会首先评估任何特定的表达式,也不保证它不会不时更改该顺序(同一发行版中是否有新的补丁程序级别?)。


+1
太好了

1
区别在于该函数是在where,select还是其他子句中调用的。select部分中的函数不会影响优化器的决定(除非它是子查询),因此在获取结果之前不需要对它们进行评估。不过where子句中的函数会影响所使用的join方法,因此需要尽快进行评估。
克里斯·萨克森

@Chris是有经验的演讲者,还是您从某个地方的文档中得到的?
杰克说试试topanswers.xyz 2012年

我找不到文档参考。根据我的经验,如果在where子句中调用它来过滤单个表,则将对每一行(假定为FTS)进行访问,但仅在选择列表中返回的行才可以访问。由于执行计划是在解析期间设置的,因此这意味着select中的函数不会对其产生影响。可以通过创建一个设置计数器(在包或表中)的函数并根据其在查询中的位置比较输出来完成一个测试用例,以检查此情况。
克里斯·萨克森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.