具有“WITH FUNCTION”定义的 MERGE 语句

mik*_*utu 4 sql oracle plsql oracle12c

我正在尝试将WITH 子句中声明的函数使用到MERGE 语句中。这是我的代码:

create table test
  (c1 varchar2(10),
   c2 varchar2(10),
   c3 varchar2(10));



insert into test(c1, c2) values ('a', 'A');
insert into test(c1, c2) values ('b', 'A');

select * from test;

begin
with function to_upper(val varchar2) return varchar is
begin
    return upper(val);
end;
merge into test a
    using (select * from test) b
on (upper(a.c1) = upper(b.c2))
when matched then 
    update set a.c3 = to_upper(a.c1);
end; 
Run Code Online (Sandbox Code Playgroud)

但我收到此错误:

错误报告 - ORA-06550:第 2 行,第 15 列:PL/SQL:ORA-00905:缺少关键字 ORA-06550:第 2 行,第 1 列:PL/SQL:忽略 SQL 语句 ORA-06550:第 6 行,第 1 列: PLS-00103:遇到符号“MERGE”06550。00000 -“行 %s,列 %s:\n%s” *原因:通常是 PL/SQL 编译错误。*行动:

有人可以解释一下为什么它不起作用吗?

谢谢你,

Ale*_*ole 5

with子句是select语法的一部分。正如铁路图所示,该子句之后唯一有效的withselect关键字 - 您没有该关键字,因此您会看到错误。

正如文档还说的那样:

plsql_declarations子句允许您声明和定义 PL/SQL 函数和过程。然后,您可以在指定此子句的查询中引用 PL/SQL 函数及其子查询(如果有)。

如果指定此子句的查询不是顶级 SELECT 语句,则以下规则适用于包含该查询的顶级 SQL 语句:

  • 如果顶级语句是 SELECT 语句,则它必须具有WITH plsql_declarations子句或WITH_PLSQL 提示。
  • 如果顶级语句是 DELETE、MERGE、INSERT 或 UPDATE 语句,则它必须具有WITH_PLSQL 提示。

因此,您不能将该with子句应用于整个merge语句,只能将其用作该语句中查询的一部分,即在子句中using

merge /*+ WITH_PLSQL */ into test a
    using (
        with function to_upper(val varchar2) return varchar is
        begin
            return upper(val);
        end;
        select to_upper(c2) as c2 from test
    ) b
on (upper(a.c1) = b.c2)
when matched then 
    update set a.c3 = upper(a.c1);
Run Code Online (Sandbox Code Playgroud)

如果没有/*+ WITH_PLSQL */提示,则会出现“ORA-32034:不支持使用WITH子句”的错误(尽管只有当您实际调用该函数时,否则编译器似乎会删除未使用的函数并且不会抱怨它)。

但该函数仍然仅在该using子句的范围内;您不能在onorupdate子句中引用它。您需要在using子句中执行任何函数调用;如果您需要将相同的函数应用于目标表中的任何内容,则需要重复该函数并使用子句调用该函数into,例如:

merge /*+ WITH_PLSQL */ into (
        with function to_upper(val varchar2) return varchar is
        begin
            return upper(val);
        end;
        select to_upper(c1) as c1, c3 from test
    ) a
    using (
        with function to_upper(val varchar2) return varchar is
        begin
            return upper(val);
        end;
        select to_upper(c2) as c2 from test
    ) b
on (a.c1 = b.c2)
when matched then 
    update set a.c3 = a.c1;
Run Code Online (Sandbox Code Playgroud)

db<>fiddle显示有效和无效的内容。

这是一个非常人为的示例,因此如果有更真实的场景,它可能会更清晰。