【问题标题】:'PLS-00103 Encountered the symbol "+" ...' error when trying to concatenate some strings in a trigger'PLS-00103 Encountered the symbol "+" ...' 尝试连接触发器中的某些字符串时出现错误
【发布时间】:2015-06-18 15:36:34
【问题描述】:

我有一个 SQL 表,其中特定的 record ID(主键)可能有多个 Contact IDs。我想创建一个触发器,每次添加新行时,它都会检查record ID 是否存在,如果存在,它将新的Contact ID 添加到当前Contact ID 字段中,用逗号分隔(到本质上是创建一个列表)。因此,对于多个Contact IDs,该字段可能变为:

Contact123, Contact456, Contact999

为此,我编写了以下触发器:

CREATE OR REPLACE
TRIGGER "TEST_INSERT" BEFORE
   INSERT ON "TEST_INSERT" REFERENCING NEW AS NEW OLD AS OLD FOR EACH ROW DECLARE REC_COUNT INTEGER;
BEGIN
   SELECT COUNT(*)
   INTO REC_COUNT
   FROM TEST_INSERT
   WHERE RECORDID = :NEW.RECORDID;
   IF REC_COUNT > 0 THEN
      CONTACTID + ', ' + :NEW.CONTACTID AS CONTACTID;   --This is the line where I'm getting the error
   END IF;
END;

+ 符号和 AS 操作出现错误。我是不是完全走错了路?

谢谢

【问题讨论】:

  • Oracle 字符串通常使用|| 运算符而不是+ 连接
  • The concatenation operator is ||;您还需要将连接的字符串分配给某些东西。不过,你在做什么并没有真正的意义。如果您插入一个新联系人,您想将该新值更改为旧值新值吗?或者您想将插入更改为更新,以便您只有一行,从而破坏规范化?你几乎肯定不想这样做,如果你这样做,你就不能使用触发器。你提到了一个PK;联系人在子表中吗?
  • @tvm:这与 Oracle 无关。这是 SQL 标准中定义的运算符。
  • 感谢您的回复。我用 + 和 || 都试过了它们都出现相同的 PL-00103 错误。亚历克斯,我正在尝试将 CONTACTID 的新值添加到与 RECORDID 匹配的行上。我想最终得到一个联系人 ID 列表,而不是为每个单独的行。例如,在 Javascript 中,我会以 contactId=contactId+", "+newContactId 的形式执行此操作,这就是我要复制的内容。
  • 如果要将新值附加到现有行中的值,则需要更新该行,而不是插入新行。您可以使用合并语句“插入”,但不能使用触发器 - 首先使用合并而不是插入。不过,我真的会质疑你的方法;您正在对数据进行非规范化 - 为什么不为每个 recordid/contactid 组合创建一个单独的表格?当您想更改或删除联系人时,您会怎么做?

标签: sql triggers oracle-sqldeveloper


【解决方案1】:

您不能将插入更改为触发器中的更新。无论如何,您的错误行中有几个问题:

CONTACTID + ', ' + :NEW.CONTACTID AS CONTACTID;

除了使用 + 代替 ||对于串联,您没有将该新值分配给变量 - CONACTID 在您正在工作的 PL/SQL 范围内不存在,尽管您可能会将串联作为更新的一部分(没有 @ 987654325@ 子句),您不能在 before-insert for-each-row 插入触发器中执行该更新,因为您会遇到变异表错误,即使这样插入仍然会发生。

看起来您确实想要执行“更新插入” - 如果现有记录存在则更新,如果不存在则添加新记录。在 Oracle 中,您可以使用 the merge statement 执行此操作。这是一个示例,我将其包装在一个过程中,只是为了使调用更清晰并减少重复——它通常只是普通的 SQL,而不是嵌入到 PL/SQL 中:

create table test_merge (recordid number primary key,
  contactid varchar2(4000));

create procedure p_merge(p_recordid number, p_contactid number) as
begin
  merge into test_merge t
  using (select p_recordid as recordid, p_contactid as contactid from dual) x
  on (t.recordid = x.recordid)
  when matched then
    update set contactid = t.contactid ||','|| x.contactid
  when not matched then
    insert (recordid, contactid) values (x.recordid, x.contactid);
end;
/

exec p_merge (1, 123);
exec p_merge (1, 234);
exec p_merge (1, 456);
exec p_merge (2, 567);

select * from test_merge;

  RECORDID CONTACTID          
---------- --------------------
         1 123,234,456         
         2 567                 

但存储以逗号分隔的非规范化数据列表并不是一个好主意。您需要考虑如何修改该值(例如,如果您必须删除联系人,或者希望它们被订购)以及如何使用字符串中的值 - 假设每个联系人 ID 都是对另一个联系人的引用表,您必须解构字符串才能进行连接。您也受到限制,但字符串列的最大大小。

最好有一个单独的表来链接记录和联系人 ID:

create table test_parent (recordid number primary key);
create table test_child (recordid number references test_parent(recordid),
  contactid number);

insert into test_parent (recordid) values (1);
insert into test_parent (recordid) values (2);
insert into test_child (recordid, contactid) values (1, 123);
insert into test_child (recordid, contactid) values (1, 234);
insert into test_child (recordid, contactid) values (1, 456);
insert into test_child (recordid, contactid) values (2, 567);

如果您想查看逗号分隔的列表,请在需要时使用字符串聚合生成该列表。 11g 及以上有the built-in listagg function 可以做到这一点:

select p.recordid,
  listagg(c.contactid, ',') within group (order by c.contactid) as contactids
from test_parent p
left join test_child c on c.recordid = p.recordid
group by p.recordid
order by p.recordid;

  RECORDID CONTACTIDS         
---------- --------------------
         1 123,234,456         
         2 567                 

【讨论】:

  • 解释得很好。我明白您关于数据规范化的观点 - 将按照您的建议考虑实施多个表。
【解决方案2】:

在 SQL Server 和 Microsoft Access 中,您使用 + 连接,但在 oracle 中,您使用 ||连接并在 mySQL 中使用 CONCAT 函数。您确定在您的情况下您使用的是正确的连接(如果我没记错的话,PLS 表示这是一个 oracle 错误,所以您应该使用 || 而不是 +)?

【讨论】:

  • || 是由 SQL 标准定义的连接运算符,它与 Oracle 无关。 Microsoft 只是决定忽略该标准,即使它在 Access 或 SQL Server 创建之前很久就存在了。
猜你喜欢
  • 2020-06-26
  • 1970-01-01
  • 2014-04-12
  • 1970-01-01
  • 2022-07-13
  • 1970-01-01
  • 2019-08-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多