Hi I want to create a trigger which will insert values on relational table A_EQ, A_U and A_P after inserting the values to entity table ACCOUNTS. The values will the inserted into A_EQ if only the ACCOUNTS.TYPE='Equipments', into A_U if only ACCOUNTS.TYPE='Utility' and into A_P for the rest.
Whenever I'm trying to compile the trigger certain errors are showing:
Error(40,13): PLS-00049: bad bind variable 'NEW.A_U'
Error(41,13): PLS-00049: bad bind variable 'NEW.A_U'
Error(42,13): PLS-00049: bad bind variable 'NEW.A_U'
Error(45,9): PLS-00049: bad bind variable 'NEW.A_EQ'
Error(46,13): PLS-00049: bad bind variable 'NEW.A_EQ'
Error(47,13): PLS-00049: bad bind variable 'NEW.A_EQ'
Error(50,9): PLS-00049: bad bind variable 'NEW.A_P'
Error(51,13): PLS-00049: bad bind variable 'NEW.A_P'
Error(52,13): PLS-00049: bad bind variable 'NEW.A_P'
Error(57,4): PLS-00103: Encountered the symbol ";" when expecting one of the following: if
The Code for trigger is given below:
create or replace trigger accounts_relational_insert
after insert on accounts
REFERENCING NEW AS new OLD AS Old
for each row
declare
sys_month VARCHAR2 (10) ;
i_month VARCHAR2 (10);
old1 int;
curr_amount number (20,2);
curr_id varchar2 (20);
curr_txn varchar2 (20);
curr_type varchar2 (20);
begin
select to_char(SYSDATE,'Month') into sys_month from dual;
select ACCOUNTS_ID_SEQ.currval into old1 from dual;
select to_char(paid_on,'Month') into i_month from accounts where transaction_id ='Txn' || lpad(old1,9,'0');
select amount into curr_amount from accounts where transaction_id='Txn' || lpad(old1,9,'0');
select id into curr_id from accounts where transaction_id='Txn' || lpad(old1,9,'0');
select transaction_id into curr_txn from accounts where transaction_id='Txn' || lpad(old1,9,'0');
select type into curr_type from accounts where transaction_id='Txn' || lpad(old1,9,'0');
if i_month = sys_month then
if curr_type ='Utility' then
:new.a_u.transaction_id := curr_txn;
:new.a_u.u_id :=curr_id;
:new.a_u.amount := curr_amount;
else if curr_type='Equipments' then
:new.a_eq.transaction_id := curr_txn;
:new.a_eq.eq_id :=curr_id;
:new.a_eq.amount := curr_amount;
else
:new.a_p.transaction_id := curr_txn;
:new.a_p.barcode:=curr_id;
:new.a_p.amount := curr_amount;
end if;
end if;
end;
The Tables are given below:
It will be very helpful if someone tells me how to resolve this error and also if there is any other way to insert into relational tables after inserting on accounts table.
You can rearrange the trigger simply as simple as this one
CREATE OR REPLACE TRIGGER accounts_relational_insert
AFTER INSERT ON accounts
FOR EACH ROW
BEGIN
IF TO_CHAR(:new.paid_on, 'YYYYMM') = TO_CHAR(sysdate, 'YYYYMM') THEN
IF :new.type = 'Utility' THEN
INSERT INTO a_u VALUES(:new.id, :new.transaction_id, :new.amount);
ELSIF :new.type = 'Equipments' THEN
INSERT INTO a_eq VALUES(:new.id, :new.transaction_id, :new.amount);
ELSE
INSERT INTO a_p VALUES(:new.id, :new.transaction_id, :new.amount);
END IF;
END IF;
END;
/
where
the values, which are already been able to get from identifiers of
columns of the table accounts qualified with :new , to be
inserted shouldn't return from SELECT statements. Moreover, you would get mutating
trigger error for this case.
indeed, defining the local variables are not needed due to the fact
that expressed above, and also values for i_month and sys_month
will directly be derived from TO_CHAR(:new.paid_on, 'YYYYMM') and
TO_CHAR(sysdate, 'YYYYMM') respectively. Btw, do not prefer using string
expression within character conversion such as Month, since you might not
only face case sensitivity issues, but also might fail for international
studies related to language differences.
the trigger is created on accounts table, for the other tables you
should use INSERT statements, cannot use such variable assignments.
The expression REFERENCING NEW AS new OLD AS old is redundant, as
being default.
Related
create or replace trigger newcontract
before insert on contract
declare numcon int;
for each row
begin
select contractcount
into numcon from task
where task.taskid = old.taskid;
if numcon < 3 then
insert into contract values(taskid, workerid, payment);
else
dbms_output.put_line('task is full');
end if;
end;
Gives this cryptic error
Error(1,5): PLS-00103: Encountered the symbol "FOR" when expecting one of the following: begin function pragma procedure subtype type <an identifier> <a double-quoted delimited-identifier> current cursor delete exists prior
The record being inserted into contract should not be inserted if contract count for that task is about 2. So I need to check the value of contractcount for each record being inserted. I use a select statement to get the value, but I get this error.
You have more than one problem here:
declare section(the part of the trigger where you declare your variables) goes after the for each row part
OLD and NEW values are "accessed" like this : :new.column_name and :old.column_name
The :old value in before insert trigger is always null because you are inserting a new value, there is no old value, only new value.
If you want to prevent insert if some value is smaller than 3 then you can do it like this:
create or replace trigger newcontract
before insert on contract
for each row
declare
numcon int;
begin
select contractcount
into numcon
from task
where task.taskid = :new.taskid;
if numcon < 3 then
raise_application_error(-20000, 'Task is full');
end if;
end;
/
Here is a small demo
For more info please do add some more detailed description and some sample data where you show us what kind of data you want to be able to insert and why and what kind of data you do not want to insert and why.
I checked all my fields and everything is fine but i keep on getting this error
When i write this trigger on sql command it says trigger has been created but when i checked on the trigger it showed invalid
This is my trigger
create or replace trigger "Max_Crew_T1"
Before insert or update on "CREW"
for each row
declare
v_count number;
Begin
select count(*) into v_count from CREW where AirPlaneID=:NEW.AirPlaneID;
if
v_count > 5
then
Raise_Application_ERROR(-20343,'Crew number exceeded');
END IF;
END;
HERE IS THE ERROR MESSAGE;
PLS-00049: bad bind variable 'NEW.AIRPLANEID'
click on the link below for table desc
The error is because you defined the airplane id while creating the table in quotes. I.e "AirPlaneID" hence you need to do the same in the trigger.
It is not a good practice to define case-sensitive columns with quotes as it causes all these issues, its much better to define without quotes eg. create table crew (airplaneid number(6))..etc. Anyway..
Please use below:
create or replace trigger "Max_Crew_T1"
Before insert or update on "CREW"
for each row
declare
v_count number;
Begin
select count(*) into v_count from CREW where "AirPlaneID"=:NEW."AirPlaneID";
if
v_count > 5
then
Raise_Application_ERROR(-20343,'Crew number exceeded');
END IF;
END;
I have tried everything but Oracle 11g Express Edition gives me this error for the procedure.
Error(14,7): PLS-00049: bad bind variable
Here is my procedure
create or replace procedure select_member_data
AS
memberdata MEMBER%rowtype;
member_ID MEMBER.MEMBER_ID%TYPE;
first_name MEMBER.FIRST_NAME%TYPE;
last_name MEMBER.LAST_NAME%TYPE;
address MEMBER.ADDRESS%TYPE;
total_rows number(2);
BEGIN
SELECT MEMBER_ID,first_name,last_name,address
INTO :memberdata
FROM MEMBER where member_id= 1 ;
IF sql%notfound THEN
dbms_output.put_line('no records fetched');
ELSIF sql%found THEN
dbms_output.put_line( total_rows ||' fetched');
END IF;
END;
here is my table
CREATE TABLE MEMBER (
MEMBER_ID INT PRIMARY KEY,
LAST_NAME VARCHAR2(25) NOT NULL,
FIRST_NAME varchar2(25),
ADDRESS varchar2(100),
CITY varchar2(30),
PHONE varchar2(15),
JOIN_DATE date DEFAULT SYSDATE NOT NULL
);
You are doing:
SELECT MEMBER_ID,first_name,last_name,address
INTO :memberdata
...
but memberdata is a local PL/SQL variable, not a bind variable, so it should just be:
SELECT MEMBER_ID,first_name,last_name,address
INTO memberdata
...
You also aren't fetching enough columns into your %rowtype variable. You table has seven columns, so your memberdata record variable has seven fields as well; but your query is only selecting four columns - which isn't enough. (Slightly confusingly, this is reported as too many values, rather than not enough values).
You can list all of the column names, but this is a rare occasion where it may be better (or at least justifiable) to use *:
SELECT *
INTO memberdata
...
Or as you have the other variables defined already you could do:
SELECT MEMBER_ID,first_name,last_name,address
INTO member_ID, first_name, last_name, address
...
although I'd stongly recommend you don't have variable names that are the same as column names; it's common to prefix local variable with something to identify them, e.g. l_member_id.
Your logic is flawed though. If your query doesn't get exactly one row then a no-data-found or too-many-rows exception will be thrown. (You can't have more than one because of the primary key here, but you could find none). The sql%notfound check won't be reached.
In SQL Server we can use this:
DECLARE #variable INT;
SELECT #variable= mycolumn from myTable;
How can I do the same in Oracle? I'm currently attempting the following:
DECLARE COMPID VARCHAR2(20);
SELECT companyid INTO COMPID from app where appid='90' and rownum=1;
Why this is not working?
SELECT INTO
DECLARE
the_variable NUMBER;
BEGIN
SELECT my_column INTO the_variable FROM my_table;
END;
Make sure that the query only returns a single row:
By default, a SELECT INTO statement must return only one row. Otherwise, PL/SQL raises the predefined exception TOO_MANY_ROWS and the values of the variables in the INTO clause are undefined. Make sure your WHERE clause is specific enough to only match one row
If no rows are returned, PL/SQL raises NO_DATA_FOUND. You can guard against this exception by selecting the result of an aggregate function, such as COUNT(*) or AVG(), where practical. These functions are guaranteed to return a single value, even if no rows match the condition.
A SELECT ... BULK COLLECT INTO statement can return multiple rows. You must set up collection variables to hold the results. You can declare associative arrays or nested tables that grow as needed to hold the entire result set.
The implicit cursor SQL and its attributes %NOTFOUND, %FOUND, %ROWCOUNT, and %ISOPEN provide information about the execution of a SELECT INTO statement.
Not entirely sure what you are after but in PL/SQL you would simply
DECLARE
v_variable INTEGER;
BEGIN
SELECT mycolumn
INTO v_variable
FROM myTable;
END;
Ollie.
One Additional point:
When you are converting from tsql to plsql you have to worry about no_data_found exception
DECLARE
v_var NUMBER;
BEGIN
SELECT clmn INTO v_var FROM tbl;
Exception when no_data_found then v_var := null; --what ever handle the exception.
END;
In tsql if no data found then the variable will be null but no exception
ORA-01422: exact fetch returns more than requested number of rows
if you don't specify the exact record by using where condition, you will get the above exception
DECLARE
ID NUMBER;
BEGIN
select eid into id from employee where salary=26500;
DBMS_OUTPUT.PUT_LINE(ID);
END;
For storing a single row output into a variable from the select into query :
declare v_username varchare(20);
SELECT username into v_username FROM users WHERE user_id = '7';
this will store the value of a single record into the variable v_username.
For storing multiple rows output into a variable from the select into query :
you have to use listagg function. listagg concatenate the resultant rows of a coloumn into a single coloumn and also to differentiate them you can use a special symbol.
use the query as below
SELECT listagg(username || ',' ) within group (order by username) into v_username FROM users;
I am trying to write a trigger to validate if the customer/address exist before inserting but i encounter some error. Able to advise on this?
Below is the table:
Customer (CustID,CustName,DOB)
CREATE OR REPLACE TRIGGER CREATEACCOUNT
BEFORE INSERT ON ACCOUNT
FOR EACH ROW
DECLARE
newCustID varchar(10);
newPostCode int;
newStreet char;
newAccType varchar(15);
newAccStatus char(9);
newAccBalance int;
varRowCount int;
BEGIN
newCustID := :new.CustID;
SELECT COUNT(*)
INTO varRowCount
FROM Customer
WHERE CustID = newCustID;
IF (varRowCount > 0) THEN
RETURN;
END IF;
IF (varRowCount = 0) THEN
BEGIN
INSERT INTO CUSTOMER VALUES (newCustID,:new.CustName,:new.DOB);
END;
END IF;
END;
Below is the error message:
Error(27,46): PLS-00049: bad bind variable 'NEW.CUSTNAME'
Error(27,60): PLS-00049: bad bind variable 'NEW.DOB'
The trigger is defined on the ACCOUNT table. You've posted the definition of the CUSTOMER table. Unless the ACCOUNT table has columns CustName and DOB, which seems highly unlikely, you can't refer to :new.CustName or :new.DOB-- the :new record is for the row that is currently being inserted into the ACCOUNT table.
Where do you intend to get the CustName and DOB to insert into the Customer table?
Taking a step back, why is a trigger on the Account table trying to insert a row into the Customer table in the first place. That seems like an exceptionally poor design. The CustID in the Account table would presumably be a foreign key that references the Customer table. That would mean, though, that you could only insert the parent row in a trigger if you declare the constraints as deferable and defer them at the beginning of every transaction. The trigger would also generally have no way of determining the information for the Customer columns that you want to populate which is the source of the error you're getting.