Oracle宏与自定义项的区别

x33g5p2x  于 2023-03-01  发布在  Oracle
关注(0)|答案(1)|浏览(209)

在Oracle中创建“普通”UDF与创建宏有什么区别?例如,对于宏,他们给予了以下示例:

CREATE FUNCTION date_string(dat DATE) 
                    RETURN VARCHAR2 SQL_MACRO(SCALAR) IS
BEGIN
   RETURN q'{
             TO_CHAR(dat, 'YYYY-MM-DD')
          }';
END;
/

对于一个function
什么情况下宏会比普通函数更有用?什么情况下函数比宏更好用?一个函数会比另一个函数有性能优势或限制吗?

nnt7mjpx

nnt7mjpx1#

SQL和PL/SQL语言有独立的运行时引擎,这意味着每次SQL语句调用PL/SQL UDF时,都会有一个上下文切换(反之亦然)。
虽然每个开关都很快,但在一条SQL语句中调用PL/SQL函数数千次或数百万次会使其明显变慢。
SQL宏的工作方式不同。在解析时,数据库将表达式解析为语句的一部分。它在返回字符串中搜索参数名称。然后有效地使用为这些参数传递的任何文本来查找/替换这些参数名称。
例如,如果运行:

select date_string ( date_col ) 
from   some_table;

最后的SQL语句有效地:

select to_char ( date_col, 'yyyy-mm-dd' )
from   some_table;

这意味着没有运行时上下文切换。这可以带来很好的性能增益,例如:

create function date_string_macro ( dat date ) 
   return varchar2 sql_macro(scalar) is
begin
   return q'{ to_char(dat, 'yyyy-mm-dd') }';
end;
/

create function date_string_plsql ( dat date ) 
   return varchar2 is
begin
   return to_char(dat, 'yyyy-mm-dd' );
end;
/

declare
  start_time pls_integer;
begin
  start_time := dbms_utility.get_time ();
  for rws in (
    select *
    from   dual
    where  date_string_plsql ( sysdate + level ) > '2021'
    connect by level <= 1000000
  ) loop
    null;
  end loop;
  dbms_output.put_line ( 
    'PL/SQL runtime = ' || ( dbms_utility.get_time () - start_time ) 
  );
  
  start_time := dbms_utility.get_time ();
  for rws in (
    select *
    from   dual
    where  date_string_plsql ( sysdate + level ) > '2021'
    connect by level <= 100000
  ) loop
    null;
  end loop;
  dbms_output.put_line ( 
    'Macro runtime  = ' || ( dbms_utility.get_time () - start_time ) 
  );
end;
/

PL/SQL runtime = 570
Macro runtime  = 54

在这种情况下大约快10倍!
由于表达式成为SQL语句的一部分,因此优化程序可以完全看到底层表达式,这是一个附带的好处,可以产生更好的执行计划。
这使您能够在获得纯SQL性能的同时,获得PL/SQL函数(例如,公共公式、字符串格式化等)的代码重用优势。
那么,为什么不创建所有现有的PL/SQL函数宏呢?
它们之间还有其他一些重要的区别。
首先,参数求值的顺序。PL/SQL使用应用程序顺序,宏使用正常顺序。这在某些情况下会导致行为差异:

create or replace function first_not_null ( 
  v1 int, v2 int
) 
  return int as
begin
  return coalesce ( v1, v2 );
end first_not_null;
/

select first_not_null ( 1, 1/0 ) from dual;

ORA-01476: divisor is equal to zero

create or replace function first_not_null  ( 
  v1 int, v2 int
) 
  return varchar2 sql_macro ( scalar ) as
begin
  return ' coalesce ( v1, v2 ) ';
end first_not_null;
/

select first_not_null ( 1, 1/0 ) from dual;

FIRST_NOT_NULL(1,1/0)   
                       1

其次,也是更重要的一点,解析表达式 * 仅发生在SQL * 中。如果在PL/SQL中调用SQL宏,它 * 将按原样返回字符串 *:

exec dbms_output.put_line ( first_not_null ( 1, 0 ) );

coalesce ( v1, v2 )

最后,SQL宏也有一个表变量。这允许你创建模板查询,你可以传递表和列。
我在几篇博客文章中讨论过表宏,展示了如何使用表宏编写一般的每组前N个函数和可重用的CSV到行函数

相关问题