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

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

Related

How do I run a count of rows over a database link?

This code, works. It runs a row count the way you'd expect, I want to tweek it, mostly to do a count over a db_link for tables dictated as I see fit.
declare
n number;
begin
for i in (select table_name from user_tables) loop
execute immediate' select count(*) from '||i.table_name into n;
dbms_output.put_line('Table Name: '||i.table_name||' Count of Row''s: '||n);
end loop;
end;
/
So, this is the adapted code... it includes a variable with the name of the link. (The link works fine) But how to reference it is probably where I'm coming unstuck.
declare
l_dblink varchar2(100) := 'DB1';
n number;
begin
for i in (select table_name from my_tables) loop
execute immediate' select count(*) from '||i.table_name#||l_dblink into n;
dbms_output.put_line('Table Name: '||i.table_name||' Count of Row''s: '||n);
end loop;
end;
/
Can someone please have a look and tell me where I'm going wrong? I just want the SQL to pick up the table names from a local table, and then use the names to count the rows in those tables, which reside in the remote database.
Monkey is on the wrong tree and can't eat a banana.
SQL> create table my_tables (table_name varchar2(20));
Table created.
SQL> insert into my_tables values ('dual');
1 row created.
SQL> set serveroutput on
SQL> declare
2 l_dblink varchar2(100) := 'db1';
3 n number;
4 begin
5 for i in (select table_name from my_tables) -- has to be like this
6 loop -- vvv
7 execute immediate' select count(*) from '||i.table_name || '#' || l_dblink into n;
8 dbms_output.put_line('Table Name: '||i.table_name||' Count of Row''s: '||n);
9 end loop;
10 end;
11 /
Table Name: dual Count of Row's: 1
PL/SQL procedure successfully completed.

PL/SQL procedure to accept an item_num and report its description,price,and on_hand quantity is not working

This is a homework assignment in my database management class. We were given the option of using Oracle SQL Developer on campus computers, or using SSH Secure Shell (I am using the later). I am to write a procedure which accepts an item_num and reports its description, price, and on_hand quantity. (Btw: all of these come from a table named "item".) My professor has said that a few commands (listed above the procedure) must proceed the procedure. My code:
serveroutput on;
accept item_num prompt 'Enter a item_num: ';
exec reports_item_info();
create or replace procedure reports_item_info (i_item_num in
item.item_num%type) as
i_description item.descripton%type;
i_std_price item.std_price%type;
i_on_hand item.on_hand%type;
begin
select description,std_price,on_hand
into i_description,i_std_price,i_on_hand
from item
where item_num = i_item_num;
dbms_output.put_line(rtrim(i_description))||' '||
(rtrim(i_std_price))||' '||(rtrim(i_on_hand))
end;
/
I know that something isn't right, because it doesn't work. How could I alter it, so that it will work?
If you pay attention to what you type, it works; you didn't provide CREATE TABLE so I created it myself.
SQL> create table item (item_num number,
2 description varchar2(20),
3 std_price number,
4 on_hand number);
Table created.
SQL>
SQL> create or replace procedure reports_item_info
2 (i_item_num in item.item_num%type)
3 as
4 i_description item.description%type;
5 i_std_price item.std_price%type;
6 i_on_hand item.on_hand%type;
7 begin
8 select description, std_price, on_hand
9 into i_description, i_std_price, i_on_hand
10 from item
11 where item_num = i_item_num;
12
13 dbms_output.put_line(rtrim(i_description) ||' '||
14 rtrim(i_std_price) ||' '||
15 rtrim(i_on_hand)
16 );
17 end;
18 /
Procedure created.
SQL>
Testing:
SQL> insert into item values (1, 'test', 100, 10);
1 row created.
SQL> exec reports_item_info(1);
test 100 10
PL/SQL procedure successfully completed.
SQL>
Your mistakes?
it is not a DESCRIPTON but DESCRIPTION
missing semi-colon at the end of DBMS_OUTPUT.PUT_LINE call
superfluous parenthesis
[EDIT]
Try with this:
SQL> declare
2 item_num number := &enter_item_num_value;
3 begin
4 reports_item_info(item_num);
5 end;
6 /
Enter value for enter_item_num_value: 1
old 2: item_num number := &enter_item_num_value;
new 2: item_num number := 1;
test 100 10
PL/SQL procedure successfully completed.
SQL>
or
SQL> accept itnum prompt 'Enter value : ';
Enter value : 1
SQL> exec reports_item_info(&itnum);
test 100 10
PL/SQL procedure successfully completed.
SQL>
The problem is due to the usage of dbms_output.put_line make it as
dbms_output.put_line(rtrim(i_description)||' '||rtrim(i_std_price)||' '||rtrim(i_on_hand));
by removing redundant parentheses.
Put a semi-colon at the end of this statement of course.
And there's a problem in spelling of the column description.
Then, use the following :
SQL> set serveroutput on;
SQL> CREATE TABLE item(
item_num varchar2(50),
description varchar2(100),
std_price number(10,2),
on_hand varchar2(100)
);
SQL> INSERT INTO item VALUES ('AH74, BR23, CD33','abc',15.75,'defgh');
SQL> COMMIT;
SQL> create or replace procedure reports_item_info(i_item_num in item.item_num%type) as
i_description item.description%type;
i_std_price item.std_price%type;
i_on_hand item.on_hand%type;
begin
select description, std_price, on_hand
into i_description, i_std_price, i_on_hand
from item
where item_num = i_item_num;
dbms_output.put_line(rtrim(i_description) || ' '|| rtrim(i_std_price)||' '||rtrim(i_on_hand));
end;
/
SQL> accept item_num prompt 'Enter a item_num: '; -- enter AH74, BR23, CD33 when prompts
SQL> exec reports_item_info('&item_num'); -- use parameter with quotes
abc 15.75 defgh
PL/SQL procedure successfully completed

Trigger — is invalid and failed re-validation

I am trying to create this simple trigger :
CREATE OR REPLACE TRIGGER my_trig
BEFORE DELETE OR INSERT OR UPDATE ON empcopy
FOR EACH ROW
WHEN (NEW.EID > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :NEW.salary - :OLD.salary;
dbms_output.put_line('Old salary: ' || :OLD.salary);
dbms_output.put_line('New salary: ' || :NEW.salary);
dbms_output.put_line('Salary difference: ' || sal_diff);
COMMIT;
END;
/
on execution it gives the result as:
Trigger created
But when I update my table the following result is obtained:
update empcopy
set salary=salary+5000;
after execution:
error at line 1
ORA-0498:triiger 'HR.MY_TRIG' is invalid and failed re-validation.
You can't COMMIT without using a PRAGMA AUTONOMOUS_TRANSACTION in your DECLARE block.
If the database allowed committing in row-level triggers, this could enable committing part of the rows in a statement before the others had been evaluated and would disrupt the statement as a unit--all rows in an individual statement should complete their change and be committed together.
If you were to use an AUTONOMOUS_TRANSACTION in this trigger, this would allow the trigger to execute new UPDATEs, DELETEs, etc in a transaction independent from other active DML changes in the open transaction.
But note, in you're case, your TRIGGER isn't actually executing any mutational DML, nor even any reads, so you don't need a COMMIT at all. All you need to do is drop your COMMIT as below.
CREATE OR REPLACE TRIGGER my_trig
BEFORE DELETE OR INSERT OR UPDATE ON empcopy
FOR EACH ROW
WHEN (NEW.EID > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :NEW.salary - :OLD.salary;
dbms_output.put_line('Old salary: ' || :OLD.salary);
dbms_output.put_line('New salary: ' || :NEW.salary);
dbms_output.put_line('Salary difference: ' || sal_diff);
END;
/
I would however suggest a couple other changes as well.
As other triggers can also change the NEW value, you might consider making this an AFTER TRIGGER, to only log the final state.
Also this trigger will not log any DELETEs, since DELETEs will all have a NULL :NEW.EID. I'd suggest either dropping the AFTER DELETE if logging DELETEs is not intended, or handling DELETEs separately via a CASE WHEN DELETING statement.
CREATE OR REPLACE TRIGGER MY_TRIG
AFTER DELETE OR INSERT OR UPDATE ON EMPCOPY
FOR EACH ROW
DECLARE
SAL_DIFF NUMBER;
BEGIN
CASE WHEN DELETING
THEN
DBMS_OUTPUT.put_line('Log the delete here if you want.');
WHEN (:NEW.EID > 0)
THEN
SAL_DIFF := COALESCE(:NEW.SALARY, 0) - COALESCE(:OLD.SALARY, 0);
DBMS_OUTPUT.put_line('Old salary: ' || :OLD.SALARY);
DBMS_OUTPUT.put_line('New salary: ' || :NEW.SALARY);
DBMS_OUTPUT.put_line('Salary difference: ' || SAL_DIFF);
ELSE NULL;
END CASE;
END;
/
Also DBMS_OUTPUT is transient logging. In case you want to keep a record of changes to EMPCOPY more permanently, Oracle has tools available to automate and control change-tracking in your data, such as audit trail and FGA.
EDIT: examples below.
Create a test table:
CREATE TABLE EMPCOPY(
EID NUMBER NOT NULL,
SALARY NUMBER
);
Table EMPCOPY created.
Then create the trigger:
CREATE OR REPLACE TRIGGER MY_TRIG
AFTER DELETE OR INSERT OR UPDATE ON EMPCOPY
FOR EACH ROW
DECLARE
SAL_DIFF NUMBER;
BEGIN
CASE WHEN DELETING
THEN
DBMS_OUTPUT.put_line('Log the delete here if you want.');
WHEN (:NEW.EID > 0)
THEN
SAL_DIFF := COALESCE(:NEW.SALARY, 0) - COALESCE(:OLD.SALARY, 0);
DBMS_OUTPUT.put_line('Old salary: ' || :OLD.SALARY);
DBMS_OUTPUT.put_line('New salary: ' || :NEW.SALARY);
DBMS_OUTPUT.put_line('Salary difference: ' || SAL_DIFF);
ELSE NULL;
END CASE;
END;
/
Trigger MY_TRIG compiled
Then test it:
SQL> --Should not log, EMPID is not greater than zero.
SQL> INSERT INTO EMPCOPY VALUES (-13, 50000);
1 row inserted.
SQL> --Should log, EMPID is greater than zero.
SQL> INSERT INTO EMPCOPY VALUES (1919, 75000);
Old salary:
New salary: 75000
Salary difference: 75000
1 row inserted.
SQL> -- The statement you provided. This should log for EMPID=1919 but not EMPID=-13
SQL> update empcopy
2 set salary=salary+5000;
Old salary: 75000
New salary: 80000
Salary difference: 5000
2 rows updated.
SQL> -- This should log a PLACEHOLDER value for each row on delete.
SQL> DELETE FROM EMPCOPY;
Log the delete here if you want.
Log the delete here if you want.
2 rows deleted.

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

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>

Creating a trigger generating ID column value before insert when new tables is created

When a table create in schema (MYSCHEMA), I need to create a trigger that generate a ID column (from sequence) before insert in each created table..
How can I realize this?
I know, how I can realize generation of ID column through trigger and sequence, something like this:
CREATE OR REPLACE TRIGGER TR1
BEFORE INSERT ON TB1
FOR EACH ROW
BEGIN
SELECT SQ1.nextval
INTO :new.primary_key_column
FROM dual;
END;
But I don't know, how I can use AFTER CREATE ON SCHEMA trigger to create trigger after CREATE TABLE in my schema with BEFORE INSERT...
I've written this code:
CREATE OR REPLACE TRIGGER /*APPROOT*/after_create_table_trigger
AFTER CREATE ON APPROOT.SCHEMA
DECLARE
TABLE_NAME VARCHAR2(100);
BEGIN
IF ORA_DICT_OBJ_TYPE = 'TABLE' THEN
SELECT ORA_DICT_OBJ_NAME INTO TABLE_NAME FROM DUAL;
EXECUTE IMMEDIATE
('CREATE OR REPLACE TRIGGER id_table_gen
BEFORE INSERT ON ' || TABLE_NAME ||
' FOR EACH ROW
BEGIN
SELECT APPROOT.AE_IDSEQ.NEXTVAL
INTO :new.ID
FROM dual;
END;');
END IF;
END;
/
Then I've created test table with one field - ID, but my trigger doesn't work...
I think the reason is wrong using of event attribute function ora_dict_obj_name.
Could somebody give me advice about this?
Thank you.
works ok if i put the schema name in the DDL.
SQL> connect sys/test as sysdba
Connected.
SQL> CREATE OR REPLACE TRIGGER after_create_table_trigger
2 AFTER CREATE ON TEST.SCHEMA
3 DECLARE
4 TABLE_NAME VARCHAR2(100);
5 BEGIN
6 IF ORA_DICT_OBJ_TYPE = 'TABLE' THEN
7 SELECT ORA_DICT_OBJ_NAME INTO TABLE_NAME FROM DUAL;
8 EXECUTE IMMEDIATE
9 ('CREATE OR REPLACE TRIGGER ID_TABLE_GEN
10 BEFORE INSERT ON TEST.' || TABLE_NAME ||
11 ' FOR EACH ROW
12 BEGIN
13 SELECT TEST.AE_IDSEQ.NEXTVAL
14 INTO :new.ID
15 FROM dual;
16 END;');
17 END IF;
18 END;
19 /
Trigger created.
SQL> connect test/test
Connected.
SQL> create table mytab(id number primary key, a varchar2(1));
Table created.
SQL> insert into mytab (a) values ('a');
1 row created.
SQL> select * From mytab;
ID A
---------- -
1 a
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE 10.2.0.4.0 Production
TNS for Linux: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production
p.s. no need to do
SELECT ORA_DICT_OBJ_NAME INTO TABLE_NAME FROM DUAL;
just paste it into the command.
CREATE OR REPLACE TRIGGER ID_TABLE_GEN
BEFORE INSERT ON APPROOT.' || ORA_DICT_OBJ_NAME ||