You can't change an insert into an update in a trigger. There are several issues in your erroring line anyway:
CONTACTID + ', ' + :NEW.CONTACTID AS CONTACTID;
As well as using + instead of || for concatenation, you are not assigning that new value to a variable - CONACTID
doesn't exist in the PL/SQL scope you're working in, and although you could potentially do the concatenation as part of an update (without the AS
clause either), you can't do that update inside the before-insert for-each-row insert trigger as you'd hit a mutating table error, and even if that worked the insert would still happen as well.
It looks like you really want to do an 'upsert' - update an existing record if it exists, or add a new record if it does not. In Oracle you do that with the merge
statement. Here's an example, which I've wrapped in a procedure just to make the calls cleaner and reduce duplication - it would normally just be plain SQL rather than embedded in 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
But storing a comma-separated list of denormalised data is not a good idea. You need to think about how you will modify that value (if you have to remove a contact, say, or want them to be ordered) and how you will use the values in the string - assuming each contact ID is a reference out to another table you'd have to deconstruct the string to do the join. You're also limited but the maximum size of a string column.
It would be much better to have a separate table linking the record and contact IDs:
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);
If you want to see the comma-separated list then generate that when you need it, with string aggregation. 11g and upwards have the built-in listagg
function to do this:
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