Error while updating a table with CLOB column:ORA-01461 - sql

I have a table in Oracle Database as follows,
create table test_clob(
id1 number,
clob_col clob);
If i try to insert a varchar2 variable with size more than 4000 into the CLOB column, it inserts without any problem.
insert into test_clob values (1,rpad('a',32760,'a'));
commit;
If i try to update the CLOB column as follows, it works perfectly fine.
update test_clob set clob_col = rpad('b',32760,'b') where id1 = 1;
commit;
However, if i try to run the update statement as follows, it is failing due to "ORA-01461: can bind a LONG value only for insert into a LONG column" error.
declare
large_string varchar2(32767) := rpad('c',32760,'c');
begin
update test_clob set clob_col = nvl(large_string,clob_col) where id1 = 1;
commit;
end;
I suspect it is the NVL function that is causing the problem. Any help on this is highly appreciated.
Note: I have used a simple table in the example, but actually the table has several columns and update statement has to update many colums at a time.

actually, rpad('a',32760,'a') when called from SQL would only return a 4k string which is why it works.
A Varchar type in SQL is limited to 4k, so when you try to bind a 32k varchar2 variable from pl/sql it will fail (as rpad when called from pl/sql will return the 32k).
eg:
SQL> select length(rpad('a',32760,'a')) from dual;
LENGTH(RPAD('A',32760,'A'))
---------------------------
4000
it silently limits the return to 4k for you. but pl/sql will not limit to 4k:
SQL> declare
2 large_string varchar2(32767) := rpad('c',32760,'c');
3 begin
4 dbms_output.put_line(length(large_string));
5 end;
6 /
32760
You should define your pl/sql variable as clob and NOT varchar2(32760):
SQL> create table test_clob(
2 id1 number,
3 clob_col clob);
Table created.
SQL> insert into test_clob values (1,rpad('a',32760,'a'));
1 row created.
SQL> select length(clob_col) from test_clob;
LENGTH(CLOB_COL)
----------------
4000
SQL> commit;
Commit complete.
SQL> declare
2 large_string clob := rpad('c',32760,'c');
3 begin
4 update test_clob set clob_col = nvl(large_string,clob_col) where id1 = 1;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> select length(clob_col) from test_clob;
LENGTH(CLOB_COL)
----------------
32760
SQL>

Related

Alter sequence from a trigger

I have an auto increment trigger that goes something like as follows
CREATE OR REPLACE TRIGGER test_auto_inc
BEFORE INSERT ON TESTTABLE
FOR EACH ROW
BEGIN
IF :NEW.TestCol IS NULL THEN
SELECT Auto_Increment.nextval INTO :new.TestCol FROM dual;
END IF;
END;
and I want to add in an if statement that will somehow lead to a decrement in the sequence I am using which is as follows:
CREATE SEQUENCE Auto_Increment START WITH 1
INCREMENT BY 1
CACHE 100;
I need to decrement the count somehow like this
ALTER SEQUENCE Auto_Increment INCREMENT BY -1;
SELECT Auto_Increment.NEXTVAL FROM dual;
ALTER SEQUENCE Auto_Increment INCREMENT BY 1;
What is the best way to do this?
Thank you
That's most probably not a good idea. What real problem are you trying to solve?
Anyway, as you asked, here's one option which does that.
This is what you currently have:
SQL> create table testtable (testcol number, datum date);
Table created.
SQL> create sequence auto_increment start with 1 increment by 1 cache 100;
Sequence created.
Trigger:
SQL> create or replace trigger test_auto_inc
2 before insert on testtable
3 for each row
4 declare
5 l_seq number;
6 begin
7 l_seq := auto_increment.nextval;
8 if l_seq >= 3 then
9 execute immediate 'alter sequence auto_increment increment by -1';
10 l_seq := auto_increment.nextval;
11 execute immediate 'alter sequence auto_increment increment by 1';
12 end if;
13
14 :new.testcol := nvl(:new.testcol, l_seq);
15 end;
16 /
Trigger created.
Testing:
SQL> insert into testtable (datum) values (sysdate);
1 row created.
SQL> insert into testtable (datum) values (sysdate);
1 row created.
SQL> select * from testtable order by datum;
TESTCOL DATUM
---------- -------------------
1 20.03.2022 08:22:17
2 20.03.2022 08:22:26
SQL>
If you want to modify (alter, eh?) the sequence within a PL/SQL procedure (that's your trigger), you'll have to use dynamic SQL as this is the only way to run a DDL from PL/SQL. OK, let's add it (I also slightly modified your code; no need for select while fetching sequence value):
SQL> create or replace trigger test_auto_inc
2 before insert on testtable
3 for each row
4 declare
5 l_seq number;
6 begin
7 l_seq := auto_increment.nextval;
8 if l_seq >= 3 then
9 execute immediate 'alter sequence auto_increment increment by -1';
10 l_seq := auto_increment.nextval;
11 execute immediate 'alter sequence auto_increment increment by 1';
12 end if;
13
14 :new.testcol := nvl(:new.testcol, l_seq);
15 end;
16 /
Trigger created.
Nice, it compiled! Why wouldn't it, Oracle has no idea what's written between single quotes. Could've been the 1st statement from Moby Dick. Let's try it:
SQL> insert into testtable (datum) values (sysdate);
insert into testtable (datum) values (sysdate)
*
ERROR at line 1:
ORA-04092: cannot COMMIT in a trigger
ORA-06512: at "SCOTT.TEST_AUTO_INC", line 6
ORA-04088: error during execution of trigger 'SCOTT.TEST_AUTO_INC'
SQL>
Whooops! Can't commit in a trigger. Bummer! Is there a way out of it? Certainly - declare the trigger to be an autonomous transaction:
SQL> create or replace trigger test_auto_inc
2 before insert on testtable
3 for each row
4 declare
5 pragma autonomous_transaction;
6 l_seq number;
7 begin
8 l_seq := auto_increment.nextval;
9 if l_seq >= 3 then
10 execute immediate 'alter sequence auto_increment increment by -1';
11 l_seq := auto_increment.nextval;
12 execute immediate 'alter sequence auto_increment increment by 1';
13 end if;
14
15 :new.testcol := nvl(:new.testcol, l_seq);
16 end;
17 /
Trigger created.
Does it work? Yes!
SQL> insert into testtable (datum) values (sysdate);
1 row created.
SQL> select * from testtable order by datum;
TESTCOL DATUM
---------- -------------------
1 20.03.2022 08:22:17
2 20.03.2022 08:22:26
3 20.03.2022 08:26:55
SQL>
Yet another insert:
SQL> insert into testtable (datum) values (sysdate);
1 row created.
SQL> select * from testtable order by datum;
TESTCOL DATUM
---------- -------------------
1 20.03.2022 08:22:17
2 20.03.2022 08:22:26
3 20.03.2022 08:26:55
3 20.03.2022 08:27:31 --> see? Yet another testcol = 3!
SQL>
So, yes ... it can be done. Note, though, that if you used autonomous transaction within a trigger for purposes different from logging, you're most probably doing it wrong.

How to catch the null constraint exception in plsql when inserting a record?

I want to check the null constraint of a column when inserting a record into a table. There, I am trying to insert a null value through a procedure to a column which cannot be null. Here is my procedure.
CREATE OR REPLACE PROCEDURE RecordInsert(s_id IN NUMBER, pid IN NUMBER, pr_id IN NUMBER, quantity NUMBER, rv DATE, exercise_num NUMBER)
IS
null_constraint EXCEPTION;
PRAGMA EXCEPTION_INIT(null_constraint,-1451);
BEGIN
INSERT INTO Supplier_Part_Project_Tab
VALUES(s_id, pid, pr_id_, quantity, rv);
dbms_output.put_line('The row successfully inserted');
COMMIT;
EXCEPTION
WHEN null_constraint THEN
dbms_output.put_line('The column cannot be NULL');
WHEN OTHERS THEN
pkg_Error.prc_Exeception(exercise_num);
END;
This is how I executed the procedure.
BEGIN
RecordInsert( 1002, '', 2001, 80, (TO_DATE('2003/05/03 09:02:44', 'yyyy/mm/dd hh12:mi:ss AM')),8);
COMMIT;
END;
In the above procedure, I am trying to insert pid as null. There, I want to fire the 'null_constraint' exception. But it fires the 'OTHERS' exception. Hope the oracle exception -1451 is correct for my requirement. But why does it not fire?
Sample table:
SQL> CREATE TABLE test
2 (
3 id NUMBER NOT NULL,
4 name VARCHAR2 (20) NOT NULL
5 );
Table created.
Procedure without exception handling section (to see what will happen):
SQL> CREATE OR REPLACE PROCEDURE RecordInsert (par_id IN NUMBER,
2 par_name IN VARCHAR2)
3 IS
4 null_constraint EXCEPTION;
5 PRAGMA EXCEPTION_INIT (null_constraint, -1451);
6 BEGIN
7 INSERT INTO test (id, name)
8 VALUES (par_id, par_name);
9
10 DBMS_OUTPUT.put_line ('The row successfully inserted');
11 /*
12 EXCEPTION
13 WHEN null_constraint
14 THEN
15 DBMS_OUTPUT.put_line ('The column cannot be NULL');
16 WHEN OTHERS
17 THEN
18 DBMS_OUTPUT.put_line ('Others');
19 */
20 END;
21 /
Procedure created.
Testing:
SQL> exec recordinsert(1, 'Littlefoot');
The row successfully inserted
PL/SQL procedure successfully completed.
SQL> exec recordinsert(2, null);
BEGIN recordinsert(2, null); END;
*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("SCOTT"."TEST"."NAME")
ORA-06512: at "SCOTT.RECORDINSERT", line 7
ORA-06512: at line 1
SQL>
Aha. ORA-01400 (not 1451). Let's modify the procedure, then:
SQL> CREATE OR REPLACE PROCEDURE RecordInsert (par_id IN NUMBER,
2 par_name IN VARCHAR2)
3 IS
4 null_constraint EXCEPTION;
5 PRAGMA EXCEPTION_INIT (null_constraint, -1400);
6 BEGIN
7 INSERT INTO test (id, name)
8 VALUES (par_id, par_name);
9
10 DBMS_OUTPUT.put_line ('The row successfully inserted');
11 EXCEPTION
12 WHEN null_constraint
13 THEN
14 DBMS_OUTPUT.put_line ('The column cannot be NULL');
15 WHEN OTHERS
16 THEN
17 DBMS_OUTPUT.put_line ('Others');
18 END;
19 /
Procedure created.
SQL> set serveroutput on
SQL>
SQL> exec recordinsert(2, null);
The column cannot be NULL --> here's your exception
PL/SQL procedure successfully completed.
SQL>
Error code you used was
ORA-01451: column to be modified to NULL cannot be modified to NULL
Cause: The column may already allow NULL values, the NOT NULL constraint is part of a primary key or check constraint, or an ALTER TABLE MODIFY statement attempted to change a column specification unnecessarily, from NULL to NULL.
Action: If a primary key or check constraint is enforcing the NOT NULL constraint, then drop that constraint.
It is related to ALTER TABLE statement, not to inserts which try to insert NULL values into NOT NULL columns, e.g.
SQL> create table test (id number not null, name varchar2(20));
Table created.
SQL> desc test
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NOT NULL NUMBER
NAME VARCHAR2(20)
SQL> alter table test modify name null;
alter table test modify name null
*
ERROR at line 1:
ORA-01451: column to be modified to NULL cannot be modified to NULL
SQL>

Code to execute select on list of multiple Oracle databases

Need your or guidance on how I can execute a select on multiple databases provided in the list. the goal behind this code is to query multiple remote databases and insert the output in current database.
Need to db_link to be fetched from a list or table
insert into xxxx.DB_tracker value(SELECT d.name FROM v$database#**opXXX_du**);
Dynamic SQL.
Suppose that database links are stored in the link table:
SQL> select * From links;
LINK
---------
dbl_ora10
dbl_ora11
dbl_orcl
You'd then use a loop, create an insert statement and execute it. As I don't have those database links, I'm just displaying statements to the screen. You'd uncomment the execute immediate line.
SQL> set serveroutput on
SQL> declare
2 l_str varchar2(200);
3 begin
4 for cur_r in (select link from links) loop
5 l_str := 'insert into db_tracker ' ||
6 'select name from v$database#' || cur_r.link;
7 dbms_output.put_line(l_str);
8
9 -- execute immediate l_str;
10 end loop;
11 end;
12 /
insert into db_tracker select name from v$database#dbl_ora10
insert into db_tracker select name from v$database#dbl_ora11
insert into db_tracker select name from v$database#dbl_orcl
PL/SQL procedure successfully completed.
SQL>
If you want to actually select name and display it on the screen, then you need the into clause. Something like this:
SQL> set serveroutput on
SQL>
SQL> declare
2 l_name varchar2(30);
3 begin
4 for cur_r in (select link from links) loop
5 execute immediate 'select name from v$database#' || cur_r.link
6 into l_name;
7 dbms_output.put_line(l_name);
8 end loop;
9 end;
10 /
XE
PL/SQL procedure successfully completed.
SQL>

Using Cursor in oracle SQL

I want to perform the delete operation in 2 tables with a given id here is sudo code
declare
cursor del_id is
select person_id from table_1 where termination is true
begin
for id_x in del_id
delete from table_X where id=id_x
delete from tabele_Y where id=id_x
How to do that ? i can't directly use my cursor please help.
I just try to print my id
begin
for id in del_id
LOOP
dbms_output.put_line(id);
END LOOP;
end;
Getting this error
Error report -
ORA-06550: line 11, column 3:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
To print the values from a cursor, you need to explicitly write the columns you want; dbms_output.put_line can not handle a row that may contain many columns with different types, so you need to pass it a string.
SQL> declare
2 cursor del_id is select 1 as one, 2 as two from dual;
3 begin
4 FOR id IN del_id
5 LOOP
6 dbms_output.put_line(id.one || ' - ' || id.two);
7 END LOOP;
8 end;
9 /
1 - 2
PL/SQL procedure successfully completed.
If you need to use the values from a cursor in some statement, a DELETE in your question, you need to do the same, by explicitly writing the column name; for example:
declare
cursor del_id is select 1 as one, 2 as two from dual;
begin
FOR id IN del_id
LOOP
delete from someTable where someColumn = id.one;
END LOOP;
end;
You do not need a cursor:
BEGIN
DELETE FROM table_X
WHERE id IN ( select person_id from table_1 where termination is true );
DELETE FROM table_Y
WHERE id IN ( select person_id from table_1 where termination is true );
END;
/
You could also use a collection:
CREATE TYPE id_type IS TABLE OF INTEGER;
/
DECLARE
ids id_type;
BEGIN
SELECT person_id
BULK COLLECT INTO ids
FROM table_1
WHERE termination is true;
DELETE FROM table_X
WHERE id MEMBER OF ids;
DELETE FROM table_Y
WHERE id MEMBER OF ids;
END;
/

SP2-0552: Bind Variable "NEW" is not declared

I am trying to learn pl/sql triggers. I am trying to create a simple trigger by tracking tutorial http://www.tutorialspoint.com/plsql/plsql_triggers.htm but I got below error. I searched on the internet but could not find the solution. Could you help me on this issue?
CREATE OR replace TRIGGER display_salary_changes
BEFORE DELETE OR INSERT OR UPDATE ON ok.customers
FOR EACH ROW
DECLARE
sal_diff NUMBER;
BEGIN
sal_diff := :NEW.salary - :OLD.salary;
END;
/
Trıgger DISPLAY_SALARY_CHANGES created.
SP2-0552: Bind Variable "NEW" is not declared.
PL/SQL procedure successfully completed.
Edit: I am using Sql Developer Version 4.1.1
Works for me (example from your link, but it's basically the same as your post):
SQL> create table demo (id integer, salary number);
Table created.
SQL> create or replace trigger display_salary_changes
2 before delete or insert or update on demo
3 for each row
4 when (new.id > 0)
5 declare
6 sal_diff number;
7 begin
8 sal_diff := :new.salary - :old.salary;
9 dbms_output.put_line('Old salary: ' || :old.salary);
10 dbms_output.put_line('New salary: ' || :new.salary);
11 dbms_output.put_line('Salary difference: ' || sal_diff);
12 end;
13 /
Trigger created.
SQL> show errors
No errors.
SQL> insert into demo (id, salary) values (1, 100);
Old salary:
New salary: 100
Salary difference:
1 row created.
SQL> update demo set salary = salary * 1.1 where id = 1;
Old salary: 100
New salary: 110
Salary difference: 10
1 row updated.
In your example it shows
Trıgger DISPLAY_SALARY_CHANGES created.
which doesn't look like SQL*Plus output. What tool did you use?
After that it gives a SQL*Plus SP2-0552 error about an undefined bind variable, followed by
PL/SQL procedure successfully completed.
What procedure was that? I suspect this is the output from a script with some other step that is failing after the trigger is created.
Is the trigger valid? You can normally right-click and check properties in desktop tools, or at the SQL*Plus prompt enter
show errors trigger display_salary_changes
Try this:
CREATE OR replace TRIGGER test_trg
BEFORE DELETE OR INSERT OR UPDATE ON test
FOR EACH ROW
DECLARE
sal_diff NUMBER;
BEGIN
sal_diff := :new.d - :old.d;
END;
/
Can you please check you column name. I have tried your code below and I got output.
create table test
(
no number(10),
sal number(10)
);
CREATE OR replace TRIGGER test_tr
BEFORE DELETE OR INSERT OR UPDATE ON test
FOR EACH ROW
DECLARE
sal_diff NUMBER;
BEGIN
sal_diff := :NEW.sal - :OLD.sal;
dbms_output.put_line(sal_diff);
END;
/
insert into test values(1,100);
update test set sal=200 where no=1;
Output :
1 rows inserted.
4 rows updated.
100
100
100
100
1 rows inserted.
I think it is missing the "REFERENCING NEW AS NEW OLD AS OLD" sentence:
CREATE TRIGGER [trigger_name]
BEFORE INSERT OR UPDATE ON [table_name]
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW