【问题标题】:MERGE statement with "WITH FUNCTION" definition带有“WITH FUNCTION”定义的 MERGE 语句
【发布时间】:2022-01-12 15:22:40
【问题描述】:

我正在尝试将 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; 

但我收到此错误:

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

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

谢谢,

【问题讨论】:

  • @AlexPoole:我通过添加函数的用法来编辑代码。

标签: sql oracle plsql oracle12c


【解决方案1】:

with 子句是select 语法的一部分。正如railroad diagram 所示,在with 子句之后唯一有效的是select 关键字——你没有那个,因此你看到的错误。

正如the documentation also says 所说:

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);

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

但该函数仍然只在using 子句的范围内;您不能在 onupdate 子句中引用它。您需要在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;

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

这是一个相当人为的例子,所以如果使用更真实的场景可能会更清楚。

【讨论】:

  • 有些奇怪。您提供的代码在 dbfiddle 中完全按照您的描述工作。在我的 12c 中,它给了我 ORA-00933: SQL 命令未正确结束
  • @mikcutu - 嗯,很有趣。在 SQL Developer 中针对 12.1 数据库运行有效 - 但前提是每个合并语句都以单独的一行上的 / 终止(如 PL/SQL 块)。
  • 嗯,还是很有趣。我正在使用您的建议,在代码的最后一行添加“/”,但出现另一个错误:ORA-30926:无法在源表中获得一组稳定的行我将在明天与其他客户端和/或测试更新我的 SQL Developer。
  • @mikcutu - 为了让它工作,我必须更改测试 c2 值之一;否则 on 子句匹配多行,这会产生错误 - 本质上是因为同一源行可能会被多次更新,这可能会导致不可预测的结果。这与函数无关 - same thing happens without it 与您的原始值无关。换客户也没用。同样,很难从人为的示例中给出建议,但也许您的真实数据还有其他独特之处。
  • 没错。这是一个愚蠢的错误。但这是我的 sqllive:livesql.oracle.com/apex/f?p=590:1:5502920764807::NO:RP::
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 2011-09-03
  • 1970-01-01
相关资源
最近更新 更多