Problem with trigger in oracle - sql

I'm new in oracle and i don't know what is wrong with this trigger:
CREATE OR REPLACE TRIGGER "propuesta_casas"
BEFORE INSERT ON "PROPUESTA_TIENDA_BARRIO"
FOR EACH ROW
WHEN (new."CASASCAL" IS NULL)
BEGIN
SELECT PROPUESTA.CASAS
INTO :new."CASASCAL"
FROM PROPUESTA WHERE PROPUESTA.IDPROPUESTA=new.IDPROPUESTA ;
END;
/
Error:
PL/SQL: ORA-00904: "NEW"."IDPROPUESTA": identifider not valid

Not sure why the accepted answer has been accepted as neither it nor the attached comments seem to address the obvious issue in the posted code.
In a trigger body we reference values in the inserted row with the :NEW code word. The posted code lacks the colon when it references the column in the WHERE clause. This is what is needed:
CREATE OR REPLACE TRIGGER "propuesta_casas"
BEFORE INSERT ON "PROPUESTA_TIENDA_BARRIO"
FOR EACH ROW
WHEN (new."CASASCAL" IS NULL)
BEGIN
SELECT PROPUESTA.CASAS
INTO :new."CASASCAL"
FROM PROPUESTA
WHERE PROPUESTA.IDPROPUESTA=:new.IDPROPUESTA ;
END;
/
Incidentally, watch out for using lower case in double quotes when creating objects.
By default all Oracle names are stored in the data dictionary in upper case, but the SQL statements are case insensitive. So the following two statments refer to the same object:
select * from emp
/
select * from EMP
/
However, if we create our object with a name in mixed case or lower case and put it in double quotes it is stored in the data dictionary with that exact case. This means we have to use that exact case whenever we reference the object, in double quotes. So if we created a table with all lower case ...
create table "emp" ...
... then this statement will fail:
select * from emp
/
It has to be
select * from "emp"
/
Of course if we already have a table called EMP then the first statement would have succeeded, if would just have selected from a different table.
In the case of triggers we generally don't refer to them by name. But we have to use the case whenever we look up the trigger in the data dictionary:
select status
from user_triggers
where trigger_name = 'propuesta_casas'
/

From what you described:
Try to recompile the trigger and see what happens...
A trigger becomes invalid if the base object (ex..table) becomes invalid or altered and the trigger refers to the affected table.

Related

How to compare two values in different tables with a TRIGGER SQL?

CREATE TRIGGER Comparer_Prix
BEFORE UPDATE ON ARTICLE
FOR EACH ROW BEGIN
SELECT ARTICLE.PRIXVENTE, ARTICLE.N_PRODUIT, ARTICLE.N_FABRICANT, LIEN_FABRICANT_PRODUIT.PRIXFABRICANT, LIEN_FABRICANT_PRODUIT.N_PRODUIT, LIEN_FABRICANT_PRODUIT.N_FABRICANT
FROM ARTICLE, LIEN_FABRICANT_PRODUIT
WHERE ARTICLE.N_FABRICANT=LIEN_FABRICANT_PRODUIT.N_FABRICANT AND ARTICLE.N_PRODUIT=LIEN_FABRICANT_PRODUIT.N_PRODUIT;
IF (NEW.PRIXVENTE< PRIXFABRICANT) THEN RAISE_APPLICATION_ERROR(-20001, 'Refusé');
END IF;
END;
I am trying to create a trigger that codes the following rule: the sales price must always be higher than the manufacturing price.
Here the errors I get:
Error(2,3): PL/SQL: SQL Statement ignored
Error(2,69): PL/SQL: ORA-00904: "LIEN_FABRICANT_PRODUIT"."PRIXFABRICANT" : invalid identifier
Error(2.92): PLS-00302: The component "PRIXFABRICANT" must be declared.
Error(5,3): PL/SQL: Statement ignored
Error(5,10): PLS-00201: the identifier 'NEW.PRIXVENTE' must be declared.
It is not new, but :new (you're missing a colon).
Though, that's not the only issue here; you can't select from a table which is just being modified; mutating table error is expected. Lucky you, you don't have to select from article as you can reference its values using the same :new value.
ORA-00904 means that you've used a column name which doesn't exist in that table. As you didn't post tables' descriptions, I'm simply repeating what you wrote. Fix it yourself.
Something like this:
create or replace trigger comparer_prix
before update on article
for each row
declare
l_prixfabricant lien_fabricant_produit.prixfabricant%type;
begin
select prixfabricant
into l_prixfabricant
from lien_fabricant_produit
where n_fabricatn = :new.n_fabricant
and n_produit = :new.n_produit;
if :new.prixvente < l_prixfabricant then
raise_application_error(-20001, 'Refusé');
end if;
end;
/
Also, it wouldn't harm if you learnt how to properly format code and make it easier to read. Use table aliases (instead of those lengthy table names).
If You compare two tables use:
... WHERE ... AND EXISTS(SELECT * FROM <TAB_COMPARE>)

Creating a trigger with SELECT Function

Good day everyone,
I'm having trouble making my trigger work. As far as the functionality of the body and how it behaves, it does exactly as I intended for it to behave. However, when I start to fire the trigger, it returns an error in which Triggers should not have a SELECT statement from inside the main body. I'm still fairly new to coding and how to materialize the idea in my head into code. Hopefully someone could point me in a right direction on how change the Trigger I would like to have as a final result. Please see below script.
Update: Expected result would be whenever a user UPDATE a row and INSERT a new record via the application or job being run in the background, S1_HOVER_REPORT column would be updated with the value from the SELECT script and use the data from the S1_HOVER case result.
(Edit: I have updated the details of the problem above, added the Table being used and Error return)
Table: SITE
Column Name Type
------------------------------
ID VARCHAR2(14)
NAME VARCHAR2(70)
TYPE_CODE VARCHAR2(2)
PARENT VARCHAR2(14)
S1_HOVER_REPORT VARCHAR2(14)
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
AFTER INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
BEGIN
UPDATE (SELECT S1.ID,
S1.NAME,
S1.TYPE_CODE,
S1.PARENT AS PARENT1,
S2.PARENT AS PARENT2,
S1.S1_HOVER_REPORT,
CASE
WHEN (S1.TYPE_CODE = 'H2') THEN S1.PARENT
WHEN (S1.TYPE_CODE = 'S1') THEN S2.PARENT
ELSE S1.ID
END AS S1_HOVER
FROM SITE S1,
(SELECT ID,
NAME,
PARENT,
TYPE_CODE
FROM site
WHERE type_code='H2') S2
WHERE S1.PARENT=S2.ID
OR S1.ID = S2.PARENT) S3
SET S3.S1_HOVER_REPORT = S3.S1_HOVER;
END;
Error returned when Trigger fired:
Error report -
SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table
ORA-06512: at "MES.S1_HOVER_REPORT", line 2
ORA-04088: error during execution of trigger 'MES.S1_HOVER_REPORT'
01779. 00000 - "cannot modify a column which maps to a non key-preserved table"
*Cause: An attempt was made to insert or update columns of a join view which
map to a non-key-preserved table.
*Action: Modify the underlying base tables directly.
(Update: I have included the updated trigger and it's now compiling without any issue, but I'm having errors whenever I try updating a record)
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
BEFORE INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
DECLARE
v_S1_HOVER_REPORT VARCHAR2(14);
BEGIN
SELECT CASE
WHEN (S1.TYPE_CODE = 'H2') THEN S1.PARENT
WHEN (S1.TYPE_CODE = 'S1') THEN S2.PARENT
ELSE (S1.ID)
END AS S1_HOVER
INTO v_S1_HOVER_REPORT
FROM SITE S1,
(SELECT ID,
NAME,
PARENT,
TYPE_CODE
FROM site
WHERE type_code='H2') S2
WHERE S1.PARENT=S2.ID
OR S1.ID = S2.PARENT;
:NEW.S1_HOVER_REPORT := v_S1_HOVER_REPORT;
END;
Error report -
SQL Error: ORA-04091: table MES.SITE is mutating, trigger/function may not see it
ORA-06512: at "MES.S1_HOVER_REPORT", line 4
ORA-04088: error during execution of trigger 'MES.S1_HOVER_REPORT'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
Firstly from the error message
An attempt was made to insert or update columns of a join view which map to a non-key-preserved table.
S3 is the veiw ( you are creating the view by doing a select inside an update statment). You can try and change this to have key preservation but I really wouldn't know how.
The error suggests updating the base tables not the view. So as mentioned in the comments :old and :new are your friend.
:OLD holds all the values of the table the trigger is created on BEFORE the update (null if insert)
:NEW holds all the values of the table the trigger is created on AFTER the update / insert.....
So if I understand what you want to do correctly you would need to...
declare a variable eg v_S1_hover_report
do your select returning whatever value you need into this variable
set the value in your site table by doing
:NEW.S1_HOVER_REPORT := v_S1_hover_report
By setting this value into the :NEW object when a commit happens it will be committed to the database. This completely removes the need for an update statement in the trigger.
You can also use :NEW.id in your select statement to filter it down to the record you are updating if it is helpfull
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
AFTER INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
v_test varchar2(10);
BEGIN
select 'Your value' into v_test from dual;
:new.s1_hover_report := v_test;
END;
OR
CREATE OR REPLACE TRIGGER MESS.S1_HOVER_REPORT
AFTER INSERT OR UPDATE ON MESS.SITE
FOR EACH ROW
v_test varchar2(10);
BEGIN
select 'Your value' into :new.s1_hover_report from dual;
END;

exists condition in a trigger

I'm quite new to PL/SQL, sorry if the question is obvious
According to the TRIGGER documentation, there is a WHEN ( condition ) for triggers. I wanted to use an exists condition, which requires a subquery, however, I have the following error :
ORA-02251
00000 - "subquery not allowed here"
*Cause: Subquery is not allowed here in the statement.
*Action: Remove the subquery from the statement.
What did I miss?
My condition is the following :
CREATE OR REPLACE TRIGGER mytrigger AFTER UPDATE OF column ON THIS_TABLE
FOR EACH ROW
WHEN (NEW.status = 'approved' AND EXISTS (
SELECT * FROM JUNCTION_TABLE WHERE THIS_TABLE_ID=NEW.this_table_id AND OTHER_TABLE_ID = 'SOMETHING'))
DECLARE
BEGIN
END;
I want to check whether the row is associated to a given value, which I can only find in a junction table.
I could surely do this in the PL/SQL part of the trigger, but :
it is related to the trigger rather than the business logic in itself
I'd like to understand what I missed in the documentation and why it is not possible.
If another condition might do this, I'm also interested.
I would write this with the conditional element within the trigger itself, something like
CREATE OR REPLACE TRIGGER mytrigger AFTER UPDATE OF column ON THIS_TABLE
FOR EACH ROW
WHEN (NEW.status = 'approved')
DECLARE
BEGIN
IF EXISTS (
SELECT * FROM JUNCTION_TABLE WHERE THIS_TABLE_ID=NEW.this_table_id AND OTHER_TABLE_ID = 'SOMETHING'))
...
END;
I don't know which Oracle documentation was used. Though the Oracle 10.2 documentation doesn't mention this, in the Oracle 11.1 documentation the limitation is mentioned:
The expression in a WHEN clause must be a SQL expression, and it cannot include a subquery. You cannot use a PL/SQL expression (including user-defined functions) in the WHEN clause.
There's no alternative I can think of at the moment to checking the condition inside the trigger code, as mentioned.

SQL Triggers calling upon multiple tables

I'm creating a row-level trigger for Oracle using SQL Tools. I will attach a picture of the ERD I am working off of. I will also include my instructions, paste what code I have tried, and the error I am getting.
Description of request:
Create a row-level trigger named "TRG_USED_COST" on the USED table. The trigger should execute BEFORE a new row is inserted in the table. The insert command will provide a value for "CHEM_NUM","JOB_NUM", and "USED_QTY" that are being added to the table. Using the CHEM_NUM being used in the insert, retrieve the "CHEM_UNIT_COST" from the CHEMICAL table. Multiply the chemical unit cost times the quantity of chemicals used in this row, and include that value as the "USED_CHARGE" for the row being inserted.
My code:
CREATE OR REPLACE TRIGGER TRG_USED_COST
BEFORE INSERT OR UPDATE OF CHEM_NUM,JOB_NUM,USED_QTY ON USED
FOR EACH ROW
BEGIN
UPDATE USED
SET USED_CHARGE = CHEM_UNIT_COST * USED_QTY
WHERE CHEM_NUM IS NOT NULL;
END;
/
After I enter the code, I receive the error:
PL/SQL: ORA-00904: "CHEM_UNIT_COST": invalid identifier
I know that I'm getting the error because I did not reference the CHEMICAL table that "CHEM_UNIT_COST" is located in...but I don't know how to reference it.
You have several issues here. First, you do not "update" the target table in this case. Use the :new attribute on the trigger. Then select the cost from the other table.
CREATE OR REPLACE TRIGGER TRG_USED_COST
BEFORE INSERT OR UPDATE OF CHEM_NUM,JOB_NUM,USED_QTY ON USED
FOR EACH ROW
DECLARE
v_cost CHEMICAL.CHEM_UNIT_COST%TYPE;
BEGIN
SELECT CHEM_UNIT_COST
INTO v_cost
FROM CHEMICAL
WHERE CHEM_NUM = :new.CHEM_NUM;
:new.USED_CHARGE := v_cost * :new.USED_QTY;
END;
/

Oracle error : ORA-00905: Missing keyword

Excuting the line of SQL:
SELECT *
INTO assignment_20081120
FROM assignment ;
against a database in oracle to back up a table called assignment gives me the following ORACLE error:
ORA-00905: Missing keyword
Unless there is a single row in the ASSIGNMENT table and ASSIGNMENT_20081120 is a local PL/SQL variable of type ASSIGNMENT%ROWTYPE, this is not what you want.
Assuming you are trying to create a new table and copy the existing data to that new table
CREATE TABLE assignment_20081120
AS
SELECT *
FROM assignment
First, I thought:
"...In Microsoft SQL Server the
SELECT...INTO automatically creates
the new table whereas Oracle seems to
require you to manually create it
before executing the SELECT...INTO
statement..."
But after manually generating a table, it still did not work, still showing the "missing keyword" error.
So I gave up this time and solved it by first manually creating the table, then using the "classic" SELECT statement:
INSERT INTO assignment_20081120 SELECT * FROM assignment;
Which worked as expected. If anyone come up with an explanaition on how to use the SELECT...INTO in a correct way, I would be happy!
You can use select into inside of a PLSQL block such as below.
Declare
l_variable assignment%rowtype
begin
select *
into l_variable
from assignment;
exception
when no_data_found then
dbms_output.put_line('No record avialable')
when too_many_rows then
dbms_output.put_line('Too many rows')
end;
This code will only work when there is exactly 1 row in assignment. Usually you will use this kind of code to select a specific row identified by a key number.
Declare
l_variable assignment%rowtype
begin
select *
into l_variable
from assignment
where ID=<my id number>;
exception
when no_data_found then
dbms_output.put_line('No record avialable')
when too_many_rows then
dbms_output.put_line('Too many rows')
end;
Though this is not directly related to the OP's exact question but I just found out that using a Oracle reserved word in your query (in my case the alias IN) can cause the same error.
Example:
SELECT * FROM TBL_INDEPENTS IN
JOIN TBL_VOTERS VO on IN.VOTERID = VO.VOTERID
Or if its in the query itself as a field name
SELECT ..., ...., IN, ..., .... FROM SOMETABLE
That would also throw that error. I hope this helps someone.
If you backup a table in Oracle Database. You try the statement below.
CREATE TABLE name_table_bk
AS
SELECT *
FROM name_table;
I am using Oracle Database 12c.
Late answer, but I just came on this list today!
CREATE TABLE assignment_20101120 AS SELECT * FROM assignment;
Does the same.