Oracle 宏和 UDF 之间的区别
Difference between Oracle Macro and UDF
在 Oracle 中创建 'normal' UDF 与创建宏 UDF 有什么区别?例如,对于macro,他们举了一个例子:
CREATE FUNCTION date_string(dat DATE)
RETURN VARCHAR2 SQL_MACRO(SCALAR) IS
BEGIN
RETURN q'{
TO_CHAR(dat, 'YYYY-MM-DD')
}';
END;
/
而 function.
宏比普通函数更有用的用例是什么?什么时候使用函数比使用宏更好?有没有一个比另一个有性能优势或限制?
SQL 和 PL/SQL 语言有单独的 运行 时间引擎。这意味着每次 SQL 语句调用 PL/SQL UDF 时,都会有一个 上下文切换 (反之亦然)。
虽然每个 switch 都很快,但在 SQL 语句中调用 PL/SQL 函数数千或数百万次可能会使它显着变慢。
SQL 宏的工作方式不同。在解析时,数据库将表达式解析为语句的一部分。它在 return 字符串中搜索参数名称。然后使用您为这些参数传递的任何文本有效地执行这些 find/replace。
例如,如果您 运行:
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 宏,它 return 将字符串按原样 :
exec dbms_output.put_line ( first_not_null ( 1, 0 ) );
coalesce ( v1, v2 )
最后 SQL 宏也有一个 table 变体。这允许您创建可以将 table 和列传递给的模板查询。
我在几篇博文中讨论了 table 宏,展示了如何使用 table 宏来编写 generic top-N per group functions and reusable CSV-to-rows functions
在 Oracle 中创建 'normal' UDF 与创建宏 UDF 有什么区别?例如,对于macro,他们举了一个例子:
CREATE FUNCTION date_string(dat DATE)
RETURN VARCHAR2 SQL_MACRO(SCALAR) IS
BEGIN
RETURN q'{
TO_CHAR(dat, 'YYYY-MM-DD')
}';
END;
/
而 function.
宏比普通函数更有用的用例是什么?什么时候使用函数比使用宏更好?有没有一个比另一个有性能优势或限制?
SQL 和 PL/SQL 语言有单独的 运行 时间引擎。这意味着每次 SQL 语句调用 PL/SQL UDF 时,都会有一个 上下文切换 (反之亦然)。
虽然每个 switch 都很快,但在 SQL 语句中调用 PL/SQL 函数数千或数百万次可能会使它显着变慢。
SQL 宏的工作方式不同。在解析时,数据库将表达式解析为语句的一部分。它在 return 字符串中搜索参数名称。然后使用您为这些参数传递的任何文本有效地执行这些 find/replace。
例如,如果您 运行:
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 宏,它 return 将字符串按原样 :
exec dbms_output.put_line ( first_not_null ( 1, 0 ) );
coalesce ( v1, v2 )
最后 SQL 宏也有一个 table 变体。这允许您创建可以将 table 和列传递给的模板查询。
我在几篇博文中讨论了 table 宏,展示了如何使用 table 宏来编写 generic top-N per group functions and reusable CSV-to-rows functions