Set a value if null inserted in Oracle SQL - sql

I have created a trigger, that will automatically set the first column values as subsequent factorial numbers. However, additionally, I would like to set the second column's value as the value of first incremented by 5, in case a null value is inserted. Here's what I try right now:
create or replace trigger test_tr
before insert on myT
for each row
begin
IF :new.mNumb is null
THEN
UPDATE myT
SET mNumb = :new.tab_id + 5;
END IF;
SELECT fac(test_seq.NEXTVAL)
INTO :new.tab_id
FROM dual;
end;
But clearly I'm missing something, as nothing happens, the inserted null is still empty.

Do not re-update the table in your trigger, update the row you're given directly:
...
IF :new.mNumb is null
THEN
:new.mNumb = :new.tab_id + 5;
END IF;
...

It all works as expected, using Emmanuel's suggestion to remove the update stmt, as far as I can tell. Here's the test case I used:
drop table test;
create table test (col1 number, col2 number);
create trigger test_trg
before insert on test
for each row
begin
IF :new.col2 is null
THEN
:new.col2 := :new.col1 + 5;
END IF;
:new.col1 := dbms_random.value;
end;
/
insert into test values (1, 1);
insert into test values (1, null);
insert into test values (null, null);
commit;
select * from test;
which produces the following output:
COL1 COL2
---------- ----------
.617580128 1
.030570358 6
.555066268
Maybe if you set :new.col1 before dealing with the null col2 scenario, that would work better for you? Doing that produces:
COL1 COL2
---------- ----------
.302670917 1
.024927489 5.02492749
.667568400 5.66756840

Related

Oracle - Adding NOT NULL constraint to a currently nullable column

I have a handful of numeric fields that are currently nullable. I want to add a NOT NULL constraint to these fields and have the default value now set to 0.
Data gets pushed in to this table on a schedule. There is nothing being pushed into these fields, and they're nullable, so these certain fields are all being set to null. This is causing some trouble when we're trying to do math with these fields.
If I first run updates on these fields to set all of the current nulls to zeros, should I have any issues adding a NOT NULL with default 0 constraint on these currently nullable fields? Is there anything I should look out for?
Thanks
edit:
SET SERVEROUTPUT ON
DECLARE
P_PROJECT_NUM VARCHAR2(200);
P_FISCAL_YEAR VARCHAR2(200);
P_RESP_CENTER VARCHAR2(200);
FIELD1 INTEGER;
FIELD2 INTEGER;
FIELD3 INTEGER;
BEGIN
P_PROJECT_NUM := '123456';
P_FISCAL_YEAR := '2019_2020';
P_RESP_CENTER := '123A';
FIELD1 := 0;
FIELD2 := 0;
FIELD3 := 0;
FMS_EXTRACT.LOAD_TEMPLATE(
P_PROJECT_NUM => P_PROJECT_NUM,
P_FISCAL_YEAR => P_FISCAL_YEAR,
P_RESP_CENTER => P_RESP_CENTER,
FIELD1 => FIELD1,
FIELD2 => FIELD2,
FIELD3 => FIELD3
);
COMMIT;
You can do it like this
Add the default clause to the columns
Update the null values to 0
Add the constraint NOT NULL
My example:
SQL> create table t ( c1 number , c2 number );
Table created.
SQL> insert into t values ( 1 , null ) ;
1 row created.
SQL> insert into t values ( null , 1 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1
1
SQL> alter table t modify c1 number default 0 ;
Table altered.
SQL> alter table t modify c2 number default 0 ;
Table altered.
SQL> update t set c1 = 0 where c1 is null ;
1 row updated.
SQL> update t set c2 = 0 where c2 is null ;
1 row updated.
SQL> commit ;
Commit complete.
SQL> alter table t modify c1 number not null ;
Table altered.
SQL> alter table t modify c2 number not null ;
Table altered.
SQL> insert into t ( c1 ) values ( 2 ) ;
1 row created.
SQL> select * from t ;
C1 C2
---------- ----------
1 0
0 1
2 0
This is too long for a comment. I don't think you want a NOT NULL constraint. That would result in your inserts failing if any of the NOT NULL columns are NULL.
The simplest solution is to not change the data at all. After all, NULL appears to be a valid value, if that is how the data is coming in. Just use COALESCE() in your logic:
select coalesce(x, 0) + coalesce(y, 0)
You can encompass this logic in a view or in generated columns:
alter table t add x_notnull generated always as (coalesce(x, 0));
If you want to prevent rows coming in with NULL values, then you can filter them out in your data important process. You don't provide details, so I can't make any suggestions.
You can add a default value. For instance:
alter table modify y default 0;
Then subsequent inserts where the value is missing will be replaced by the default value. However, explicit NULL values will not be affected.
Finally, you can add a NOT NULL constraint -- after removing all current NULL values.
Here is a db<>fiddle showing some examples of the above.

Create procedure for inserting the records, if exception, procedure needs start from exception line

I need to write the procedure for inserting the records in to multiple tables, for example I have 3 table,
CREATE TABLE SOURCE
(
SORT_CODE NUMBER,
FLAG CHAR(1)
);
INSERT INTO SOURCE VALUES(605096,5);
INSERT INTO SOURCE VALUES(605097,5);
INSERT INTO SOURCE VALUES(605098,5);
INSERT INTO SOURCE VALUES(605099,5);
INSERT INTO SOURCE VALUES(605100,5);
INSERT INTO SOURCE VALUES(605101,6);
INSERT INTO SOURCE VALUES(605102,6);
INSERT INTO SOURCE VALUES(605103,6);
INSERT INTO SOURCE VALUES(605104,6);
INSERT INTO SOURCE VALUES(605105,6);
SQL> SELECT * FROM SOURCE;
SORT_CODE F
---------- -
605096 5
605097 5
605098 5
605099 5
605100 5
605101 6
605102 6
605103 6
605104 6
605105 6
10 rows selected.
CREATE TABLE TARGET
(
SORT_CODE NUMBER,
TARGET_SORT_CODE NUMBER
);
Table created.
INSERT 5 VALUES
INSERT INTO TARGET VALUES(605101,189873);
INSERT INTO TARGET VALUES(605102,189874);
INSERT INTO TARGET VALUES(605103,189875);
INSERT INTO TARGET VALUES(605104,189876);
INSERT INTO TARGET VALUES(605105,'');
SELECT * FROM TARGET;
SORT_CODE TARGET_SORT_CODE
---------- ----------------
605101 189873
605102 189874
605103 189875
605104 189876
605105
CREATE TABLE NEWID
(
SORT_CODE NUMBER,
ID_SCODE NUMBER
);
Table created.
INSERT 2 VALUES
INSERT INTO TARGET VALUES(605103,189875);
INSERT INTO TARGET VALUES(605104,189876);
SELECT * FROM NEWID;
SORT_CODE ID_SCODE
---------- ----------------
605103 189875
605104 189876
Creating intermediate tables with existing table's structure.
CREATE TABLE SOURCE_TEMP AS (SELECT * FROM SOURCE WHERE 1=2);
CREATE TABLE TARGET_TEMP AS (SELECT * FROM TARGET WHERE 1=2);
CREATE TABLE NEWID_TEMP AS (SELECT * FROM NEWID WHERE 1=2);
My Procedure for inserting the records
CREATE OR REPLACE PROCEDURE insert_sql
is
BEGIN
DELETE FROM SOURCE_TEMP;
INSERT INTO SOURCE_TEMP SELECT * FROM SOURCE; --insert query 1
DELETE FROM TARGET_TEMP;
INSERT INTO TARGET_TEMP SELECT * FROM TARGET; --insert query 2
--due to some network issue or table error this procedure GOT EXEPCTION here and above insert query 2(TARGET_TEMP) and below --insert query 3(NEWID_TEMP) is not inserted the values or not executed procedure is came out from this line.
DELETE FROM NEWID_TEMP;
INSERT INTO NEWID_TEMP SELECT * FROM NEWID; --insert query 3
EXCEPTION
WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('ERROR');
END;
Point 1: The above procedure is executed only one insert query 1 SOURCE_TEMP is got the values.
Point 1: TARGET_TEMP and NEWID_TEMP is not inserted the values or not execute.
My question: can I re-execute this procedure with starting point of '--insert query 2' line?
because I am inserting the 100 tables records in new tables, if 50 tables are inserted the values during this time if I am getting any error in the proc execution, remaining 50 tables needs to insert the values, for I don't wish to delete the previous 50 tables inserted the values it will be the time consuming activity. Any save point or boolean concepts is there for this type of issue in ORACLE (which is available in java and unix). if yes how to use this function?
CREATE OR REPLACE PROCEDURE insert_sql
is
v_new_rec_count int:=0;
BEGIN
select count(*)
into v_new_rec_count
from (
select * FROM SOURCE
minus
select * FROM SOURCE_TEMP
) ;
If v_new_rec_count >0 then
INSERT INTO SOURCE_TEMP SELECT * FROM SOURCE; --insert query 1
Commit;--permanently save the records in table and it wont be rolledback after - -- any subsequent failure.
v_new_rec_count :=0;
end if;
select count(*)
into v_new_rec_count
from (
select * FROM TARGET
minus
select * FROM TARGET_TEMP
) ;
If v_new_rec_count >0 then
INSERT INTO TARGET_TEMP SELECT * FROM TARGET; --insert query 2
Commit;--permanently save the records in table and it wont be rolledback after - -- any subsequent failure.
v_new_rec_count :=0;
end if;
select count(*)
into v_new_rec_count
from (
select * FROM NEWID
minus
select * FROM NEWID_TEMP
) ;
If v_new_rec_count >0 then
INSERT INTO NEWID_TEMP SELECT * FROM NEWID; --insert query 2
Commit;--permanently save the records in table and it wont be rolledback after - -- any subsequent failure.
v_new_rec_count :=0;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('ERROR');
END;
CREATE OR REPLACE PROCEDURE insert_sql
IS
x int;
FLG FLAG.FLAG%type;
BEGIN
dbms_output.enable;
SELECT FLAG INTO FLG FROM FLAG;
if FLG='PASS01' then
DBMS_OUTPUT.PUT_LINE('PASS01');
DELETE FROM SOURCE_TEMP;
DBMS_OUTPUT.PUT_LINE('SOURCE_TEMP-DELETED');
INSERT INTO SOURCE_TEMP SELECT * FROM SOURCE;
DBMS_OUTPUT.PUT_LINE('SOURCE_TEMP-INSERTED');
UPDATE FLAG SET FLAG='PASS02';
DBMS_OUTPUT.PUT_LINE('PASS02-updated');
SELECT FLAG INTO FLG FROM FLAG;
DBMS_OUTPUT.PUT_LINE('PASS02-SELECT');
COMMIT;
end if;
if FLG='PASS02' then
DBMS_OUTPUT.PUT_LINE('PASS02');
DELETE FROM TARGET_TEMP;
DBMS_OUTPUT.PUT_LINE('TARGET_TEMP-DELETED');
INSERT INTO TARGET_TEMP SELECT * FROM TARGET;
DBMS_OUTPUT.PUT_LINE('TARGET_TEMP-INSERTEDD');
UPDATE FLAG SET FLAG='PASS03';
DBMS_OUTPUT.PUT_LINE('PASS03-updated');
SELECT FLAG INTO FLG FROM FLAG;
DBMS_OUTPUT.PUT_LINE('PASS03-FLG');
COMMIT;
end if;
--x :=1/0;
if FLG='PASS03' then
DBMS_OUTPUT.PUT_LINE('PASS03');
DELETE FROM NEWID_TEMP;
DBMS_OUTPUT.PUT_LINE('NEWID_TEMP-DELETED');
INSERT INTO NEWID_TEMP SELECT * FROM NEWID;
DBMS_OUTPUT.PUT_LINE('NEWID_TEMP-INSERTEDD');
UPDATE FLAG SET FLAG='PASS01';
DBMS_OUTPUT.PUT_LINE('PASS01-updated');
SELECT FLAG INTO FLG FROM FLAG;
DBMS_OUTPUT.PUT_LINE('PASS01-FLG');
COMMIT;
end if;
EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('NO_DATA_FOUND!');
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Error Message: !'|| SQLERRM ||' Sql Code: ' || SQLCODE);
END;
CREATE OR REPLACE PROCEDURE insert_sql
is
v_new_rec_count int:=0;
CURSOR cur_src
IS
SELECT * FROM SOURCE ;
TYPE cur_src_typ IS TABLE OF cur_src%ROWTYPE;
cur_src_tbl cur_src_typ;
CURSOR cur_newid
IS
SELECT * FROM newid ;
TYPE cur_newid_typ IS TABLE OF cur_src%ROWTYPE;
cur_newid_tbl cur_newid_typ;
CURSOR cur_target
IS
SELECT * FROM target ;
TYPE cur_target_typ IS TABLE OF cur_src%ROWTYPE;
cur_target_tbl cur_target_typ;
BEGIN
cur_src_tbl :=cur_src_tbl();
cur_newid_tbl :=cur_newid_tbl();
cur_target_tbl :=cur_target_tbl();
OPEN cur_src;
LOOP
FETCH cur_src BULK COLLECT INTO cur_src_tbl LIMIT 500;
EXIT WHEN cur_src_tbl.count =0;
FOR i IN cur_src LOOP
BEGIN
INSERT INTO SOURCE_TEMP VALUES(cur_src(i).sort_code,cur_src(i).flag);
COMMIT;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
CLOSE cur_src;
OPEN cur_target;
LOOP
FETCH cur_target BULK COLLECT INTO cur_target_tbl LIMIT 500;
EXIT WHEN cur_target.count =0;
FOR i IN cur_target_tbl LOOP
BEGIN
INSERT INTO target_temp VALUES(cur_target_tbl(i).sort_code,cur_target_tbl(i).target_sort_code);
COMMIT;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
CLOSE cur_newid;
OPEN cur_newid;
LOOP
FETCH cur_newid BULK COLLECT INTO cur_newid_tbl LIMIT 500;
EXIT WHEN cur_newid_tbl.count =0;
FOR i IN cur_newid_tbl LOOP
BEGIN
INSERT INTO newid_TEMP VALUES(cur_newid_tbl(i).sort_code,cur_newid_tbl(i).flag);
COMMIT;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
CLOSE cur_newid;
END;
Thanks to All ur support
I created the Proc, Finally I got PLSQL-PROCEDURE to execute my concepts.
create TABLE FLAG
(
FLAG VARCHAR2(6)
);
INSERT INTO FLAG VALUES('PASS01');

After insert Trigger

I'm trying to do a trigger which insert some into column after insert statement. For example I have table with column which looks:
Column1 Column2 Column3
And I'm inserting data into Column 1, Insert into Table(Column1) values ('256234','234234').
Now I would like automatically insert into COlumn2 TImestamp and into Column3 Value "Y", So output should looks:
Column1 Column2 Column3
256234 2015-10-28 08:48 Y
234234 2015-10-28 08:48 Y
Guys, could you help me with that? I tried to use cursor
Finally I got something like that:
create or replace trigger name
after insert on table
declare
c1 sys_refcursor;
idx varchar2(200);
begin
open c1 for select Column1 from table ;
loop
fetch c1 into idx;
exit when c1%NOTFOUND;
update table a1 set a1.Column2 = (select to_char(sysdate,'YYYYMMDDHHMISS') from dual) where Column1=idx;
update table a1 set a1.Column3 = (select 'Y' from dual) where Column1=idx;
end loop;
close c1;
end;
It works fine, but I'm wondering if there is some other better solution than that?
No need for a cursor or even an update:
create or replace trigger name
before insert on table_x
begin
:new.column2 := sysdate;
:new.column3 := 'Y';
end;
/
But you need a before trigger for this, because an after trigger cannot modify the newly inserted row.
But why don't you just define a default value for those columns, then you don't need a trigger at all:
create table table_x
(
column_1 integer,
column_2 date default sysdate,
column_3 varchar(1) default 'Y'
);
You can create trigger as similar :
CREATE OR REPLACE TRIGGER "TRG_NAME"
BEFORE INSERT ON "TABLE_NAME"
FOR EACH ROW
DECLARE
BEGIN
:NEW.Column2 := to_char(sysdate,'YYYYMMDDHHMISS');
:NEW.Column3 := 'Y';
END TRG_NAME ;
/
ALTER TRIGGER "TRG_NAME" ENABLE;
/
Hope this PL/SQL will help you..

How can I "group" multiple rows into a collection in my PL/SQL cursor?

I have a cursor where I need to get a whole bunch of information for an interface, but one of the tables that I need to get information from has one row for each information that I need in columns.
I looked into PIVOT and at first it seemed like it would be a mess (especially considering that I don't need an aggregate for this) but I managed to get it to work nicely; but I'm a serial learner so I still want to find out if it's possible like this:
I thought of doing this by fetching a table as a column: "type table of (object)" so I'd have an array of an array in my PL/SQL code... and it worked fine! When my properties table had only one row, but I got ORA-01427 when it had more than one.
Here's a short example code (I'm using just the IDs and addresses tables for simplicity, this is an actual little PL I created just to test this functionality):
CREATE OR REPLACE TYPE CAIB_FIELDS AS OBJECT (
ID_QUALIFIER VARCHAR2(3),
ID_NUMBER VARCHAR2(20)
)
/
CREATE TYPE CAIB_TBL AS TABLE OF CAIB_FIELDS
/
DECLARE
CURSOR MYCUR(CID IN VARCHAR2) IS
SELECT CUST_ID,CUST_ADDR,UPD_DATE
(SELECT CAIB_TBL(CAIB_FIELDS(ID_QUALIFIER,ID_NUMBER)) FROM CUSTOMER_IDS B
WHERE B.CUST_ID = A.CUST_ID
AND B.CUST_ADDR = A.CUST_ADDR) CAIB
FROM CUSTOMER_ADDR A
WHERE A.CUST_ID = CID
;
TYPE MYCUR_TYPE IS TABLE OF MYCUR%ROWTYPE;
REC_MYCUR MYCUR_TYPE;
BEGIN
OPEN MYCUR('918888'); --This customer has only one ID row -> OK! IT WORKS!
--For customer ID '002632', he has several ID ROWS -> ERROR ORA-01427
LOOP
FETCH MYCUR BULK COLLECT INTO REC_MYCUR LIMIT 100;
FOR I IN 1..REC_MYCUR.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(REC_MYCUR(I).CAIB(1).ID_QUALIFIER);
--DBMS_OUTPUT.PUT_LINE(REC_MYCUR(I).CAIB(2).ID_QUALIFIER); --this would be OK if Oracle would allow me to fetch multiple rows on my CAIB_TBL type, of course I'll just loop here if it works, but for the sake of this test, I just used fixed values..
END LOOP;
EXIT WHEN MYCUR%NOTFOUND;
END LOOP;
END;
Thanks in advance!!
--- EDIT, tbone's answer is exactly what I was looking for, but it doesn't reflect the exact scenario as it only deals with a single column table; for multiple columns the solution changes just slightly, here's my final test:
create table testA
(
col1 number,
col2 varchar2(50)
);
create table testB
(
col1 number,
col2 varchar2(50),
col3 varchar2(50)
);
insert into testA values (1,'A');
insert into testA values (2,'B');
insert into testA values (3,'C');
insert into testB values (1,'X','x');
insert into testB values (1,'Y','y');
insert into testB values (1,'Z','z');
insert into testB values (2,'BA','ba');
insert into testB values (2,'BB','bb');
commit;
CREATE OR REPLACE TYPE t_test_rec AS object
(col2 varchar2(50),
col3 varchar2(50)
)
/
create or replace type t_vchar_tab as table of t_test_rec;
DECLARE
CURSOR MYCUR IS
SELECT A.COL1,
CAST(MULTISET(SELECT B.COL2,B.COL3 FROM TESTB B WHERE B.COL1 = A.COL1 ORDER BY B.COL2) AS T_VCHAR_TAB) AS TESTB_VALS
FROM TESTA A
;
TYPE MYCUR_TYPE IS TABLE OF MYCUR%ROWTYPE;
REC_MYCUR MYCUR_TYPE;
BEGIN
OPEN MYCUR;
LOOP
FETCH MYCUR BULK COLLECT INTO REC_MYCUR LIMIT 100;
FOR I IN 1..REC_MYCUR.COUNT
LOOP
IF REC_MYCUR(I).TESTB_VALS.COUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE(REC_MYCUR(I).COL1 || '->(NULL)');
ELSE
FOR J IN 1..REC_MYCUR(I).TESTB_VALS.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(REC_MYCUR(I).COL1 || '->' || REC_MYCUR(I).TESTB_VALS(J).COL2 || ',' || REC_MYCUR(I).TESTB_VALS(J).COL3);
NULL;
END LOOP;
END IF;
END LOOP;
EXIT WHEN MYCUR%NOTFOUND;
END LOOP;
END;
/
Its a bit unclear what you're trying to achieve, but based on the title (grouping multiple rows into a collection from a cursor), you can do something like this:
set echo on;
set display on;
set linesize 200;
create table testA
(
col1 number,
col2 varchar2(50)
);
create table testB
(
col1 number,
col2 varchar2(50)
);
create or replace type t_vchar_tab as table of varchar2(50);
insert into testA values (1,'A');
insert into testA values (2,'B');
insert into testB values (1,'X');
insert into testB values (1,'Y');
insert into testB values (1,'Z');
commit;
-- select all related testB.col2 values in a nested table for each testA.col1 value
select a.col1,
cast(multiset(select b.col2 from testB b where b.col1 = a.col1 order by b.col2) as t_vchar_tab) as testB_vals
from testA a;
So the output will be only the 2 rows from tableA, but have a nested table column containing all the matching rows from tableB

Update with after insert trigger on same table

I have a to write a insert trigger on a tableA. which will perform update with same table but different column. I am getting error while doing this. My trigger is
create or replace trigger trigger_A
after insert on table_A
begin
update table_A set col1=1 where col1 is null;
end;
I have an application will perform col2 alone will be inserted and col1 will be kept null. so my trigger will give value for col1 once the row is inserted. But i am getting error saying "Trigger is failed and invalid" when a row is inserted.
How to do this. TIA.
If you want to assign a simple default value, the easiest way is to declare it on the table, using the DEFAULT clause.
SQL> create table t42
2 ( col1 number default 1 not null
3 , col2 date)
4 /
Table created.
SQL> insert into t42 (col2) values (sysdate)
2 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2
---------- ---------
1 03-AUG-11
SQL>
This works with literals or pseudocolumns such as SYSDATE or USER. If you want to derive a more complicated value with a user-defined function or a sequence, you will need to use
a trigger.
Here is a new version of the table...
SQL> create table t42
2 ( col1 number default 1 not null
3 , col2 date default sysdate
4 , col3 varchar2(30) default user
5 , col4 number )
6 /
Table created.
SQL>
... with a trigger:
SQL> create or replace trigger t42_trg
2 before insert or update
3 on t42
4 for each row
5 begin
6 if :new.col4 is null
7 then
8 :new.col4 := my_seq.nextval;
9 end if;
10 end;
11 /
Trigger created.
SQL> insert into t42 (col1, col2, col3)
2 values (99, sysdate, 'MR KNOX')
3 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2 COL3 COL4
---------- --------- ------------------------------ ----------
99 03-AUG-11 MR KNOX 161
SQL>
Note that although every column on the table is defaultable, I have to populate at least one column to make the SQL valid:
SQL> insert into t42 values ()
2 /
insert into t42 values ()
*
ERROR at line 1:
ORA-00936: missing expression
SQL>
But I can pass in NULL to COL4 to get a completely defaulted record:
SQL> insert into t42 (col4) values (null)
2 /
1 row created.
SQL> select * from t42
2 /
COL1 COL2 COL3 COL4
---------- --------- ------------------------------ ----------
99 03-AUG-11 MR KNOX 161
1 03-AUG-11 APC 162
SQL>
Caveat lector: my trigger uses the new 11g syntax. In previous versions we have to assign the sequence value using a SELECT statement:
select my_seq.nextval
into :new.col4
from dual;
You cannot update a table where the trigger is invoked:
Within a stored function or trigger, it is not permitted to modify a
table that is already being used (for reading or writing) by the
statement that invoked the function or trigger.
Doing so will generate Error 1442:
Error Code: 1442
Can't update table 'MyTable' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
In short, we are not allowed to update the table in use - but your case is simple, only want to update the field if it's NULL, for this choose BEFORE INSERT ON trigger, this way you can update all the fields of the new/current entry/row (as it has not been entered yet):
DELIMITER //
DROP TRIGGER IF EXISTS trigger_A//
CREATE TRIGGER trigger_A BEFORE INSERT ON table_A
FOR EACH ROW BEGIN
IF NEW.col1 IS NULL THEN
set NEW.col1 = <some-value>;
ENF IF;
END;
//
DELIMITER ;