Getting "invalid NEW or OLD specification" message when I try to run this - sql

The entire question I'm trying to answer is:
Utilize the travel anywhere database to create a database trigger "hotel_kids_rule" to enforce a business rule. when inserting a hotel_reservation, if the num_kids value is more than zero, then assign the bed_type DQ to the reservation. save the trigger source as a script file.
For example, the trigger will change the bed_type for the following insert statement.
insert into hotel_reservation (reserve_no,reserve_date,arrival_date,dep_date,num_adults,num_kids,customer_id,hotel_id,bed_type,rooms)
values(hotel_reserve_sequence.nextval,sysdate,sysdate+5,sysdate+7,1,2,101,19,'DT',1);
For example, the trigger will not change the bed_type for the following insert statement.
insert into hotel_reservation (reserve_no,reserve_date,arrival_date,dep_date,num_adults,num_kids,customer_id,hotel_id,bed_type,rooms)
values(hotel_reserve_sequence.nextval,sysdate,sysdate+5,sysdate+7,2,0,102,20,'DT',1);
This is what I have so far:
create or replace trigger hotel_kids_rule
after insert or update on hotel_reservation
for each row
when (num_kids > 0)
declare
new_bed_type varchar2(20);
new_bed_type='DQ'
begin
new.bed_type=new_bed_type;
end;
Any help would be appreciated! Thanks!

I think you want a before trigger, so you can set the new value before it is written. Also, the way you access new is not ok: within the trigger code, you need :new.bed_type; in the when condition, you want new.num_kids.
This should work:
create or replace trigger hotel_kids_rule
before insert or update on hotel_reservation
for each row
when (new.num_kids > 0)
begin
:new.bed_type := 'DQ';
end;
/
Here is a small demo.

The assignment of your variable needs to be inside the actual code block. And the assignment operator is := in PL/SQL, not =. If you want the trigger to change anything you also need to create it as a BEFORE trigger:
create or replace trigger hotel_kids_rule
before insert or update on hotel_reservation
for each row
when (new.num_kids > 0)
declare
new_bed_type varchar2(20);
begin
new_bed_type := 'DQ';
:new.bed_type := new_bed_type;
end;
Note that the new record in the when condition is used without the colon : while inside the code block it needs to be referenced with the colon.
Alternatively initialize the variable when declaring it:
declare
new_bed_type varchar2(20) := 'DQ';
begin
:new.bed_type := new_bed_type;
end;
Or even simpler without any intermediate variable.
begin
:new.bed_type := 'DQ';
end;

Related

ORACLE SQL trigger and SUM()

I have a table COMMANDE and a table REGROUPE. I have that function:
CREATE OR REPLACE PROCEDURE multiplicateur(a NUMBER, taxes NUMBER, c OUT NUMBER)
IS
BEGIN
c := a * taxes ;
END multiplicateur;
/
I'm trying to make a trigger using that founction to update a total price with taxes from a command that can contain more than one item. I tried this but it doesn't want to work:
create or replace TRIGGER MAJ_PRIX_COMMANDE
AFTER INSERT OR UPDATE OR DELETE ON REGROUPE
FOR EACH ROW
DECLARE
resultat NUMBER;
BEGIN
UPDATE COMMANDE
SET COMMANDE.prixTotal = multiplicateur((CAST((SELECT SUM(prixRegroupe)FROM REGROUPE WHERE REGROUPE.numCommande = :NEW.numCommande)AS NUMBER)),1.15,resultat)
WHERE COMMANDE.numCommande = :NEW.numCommande;
END;
Can someone help me?
How about this? I sent this from my iPhone; this code hasn't been tested.
create or replace TRIGGER MAJ_PRIX_COMMANDE
AFTER INSERT OR UPDATE OR DELETE ON REGROUPE
FOR EACH ROW
DECLARE
resultat NUMBER;
l_groupe NUMBER; --will store sum based on numCommande
BEGIN
--retrieve sum of prixRegroupe
SELECT SUM(r.prixRegroupe)
INTO l_groupe
FROM regroupe r
WHERE r.numCommande = :NEW.numCommande;
--call procedure and pass the sum of priRegroupe
multiplicateur(l_groupe, 1.15, resultat);
--use precedures out argument to update commande
UPDATE COMMANDE c
SET c.prixTotal = resultat
WHERE c.numCommande = :NEW.numCommande;
END;
This code won't work. It will result in a mutating trigger error. Within a trigger you cannot query or modify the same table that the trigger is based on until after the trigger has completed execution.
ORA-04091: table name is mutating, trigger/function may not see it
Your problem right now is that you're querying the regroupe table while the trigger is trying to insert/update the same table. This cannot be accomplished.
You'll need to find a way to get the sum of the prixRegroup column some other way. It's late if I think of anything I'll respond tomorrow.

Create Before Insert Trigger in oracle and implement in asp.net web form

I have a requirement of which I want to create a trigger in Oracle.
So what I want is: I have a table called XXCUS.XXACL_PN_PROSPECT_TRL in which there is a column called ACTION whose default value is set as RELEASE, EXTEND and CANCEL.
So my trigger would be on the table XXCUS.XXACL_PN_PROSPECT_TRL as
whenever the column ACTION has values as
RELEASE then the other table column should get UPDATED as R with the same matching ID
EXTEND then the other table column should get UPDATED as O with the same matching ID
CANCEL then the other table column should get UPDATED as C with the same matching ID
The other table name is xxcus.xxacl_pn_leases_all whose column needs to be updated. Also the column name is CLOSE_FLAG.
I m trying like below
CREATE [ OR REPLACE ] TRIGGER XXACL_PN_PROSPECT_T
BEFORE INSERT
ON XXCUS.XXACL_PN_PROSPECT_TRL
[FOR EACH ROW]
DECLARE
-- variable declaration
BEGIN
-- trigger code
Insert into XXCUS.XXACL_PN_PROSPECT_TRL -- values for ACTION here
then
update xxcus.xxacl_pn_leases_all if 'RELEASE' then 'R', if 'EXTEND' then 'O' where mkey = dynamic
EXCEPTION
WHEN ...
-- exception handling
END;
but it is not working as I am not champ in creating Triggers. Kindly help me with this
There are some obvious syntax errors
You don't need square brackets. Those are in the documentation to indicate optional parts.
Your insert/update statements are wrong, need to correct them.
It should look on the lines of :
CREATE OR REPLACE TRIGGER XXACL_PN_PROSPECT_T
BEFORE INSERT
ON XXCUS.XXACL_PN_PROSPECT_TRL
DECLARE
-- variable declaration
BEGIN
UPDATE xxcus.xxacl_pn_leases_all
SET CLOSE_FLAG = DECODE(:NEW.ACTION, 'RELEASE','R','EXTEND','O', 'CANCEL','C')
WHERE ID = :NEW.ID
END;
This trigger will update CLOSE_FLAG column of xxcus.xxacl_pn_leases_all based on what value comes in Action column of XXACL_PN_PROSPECT_TRL table.
While going through the links and documentation, I tried my self and figured out the solution.
There were many syntax errors in the starting. But I guess, I was the only one to apply the exact logic and took help of the syntax online.
So here it was my TRIGGER which I wrote and it worked perfectly.
CREATE OR REPLACE TRIGGER xxcus.xxacl_pn_prospect_trl_trg
AFTER INSERT OR UPDATE
ON xxcus.xxacl_pn_prospect_trl
FOR EACH ROW
DECLARE
sql_error VARCHAR (10000);
v_mkey NUMBER;
v_action VARCHAR (10);
BEGIN
v_action := :NEW.action;
IF (v_action = 'Extend')
THEN
UPDATE xxcus.xxacl_pn_leases_all
SET no_of_days = :NEW.current_action_days
WHERE lease_num = :NEW.lease_no;
ELSIF (v_action = 'Release')
THEN
UPDATE xxcus.xxacl_pn_leases_all
SET no_of_days = 0,
close_flag = 'R'
WHERE lease_num = :NEW.lease_no;
ELSE
UPDATE xxcus.xxacl_pn_leases_all
SET no_of_days = 0,
close_flag = 'C'
WHERE lease_num = :NEW.lease_no;
END IF;
EXCEPTION
WHEN OTHERS
THEN
sql_error := SQLERRM; END;/

PostgreSQL Update trigger

I have a table:
CREATE TABLE annotations
(
gid serial NOT NULL,
annotation character varying(250),
the_geom geometry,
"rotationAngle" character varying(3) DEFAULT 0,
CONSTRAINT annotations_pkey PRIMARY KEY (gid),
CONSTRAINT enforce_dims_the_geom CHECK (st_ndims(the_geom) = 2),
CONSTRAINT enforce_srid_the_geom CHECK (st_srid(the_geom) = 4326)
)
And trigger:
CREATE TRIGGER set_angle
AFTER INSERT OR UPDATE
ON annotations
FOR EACH ROW
EXECUTE PROCEDURE setangle();
And function:
CREATE OR REPLACE FUNCTION setAngle() RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
UPDATE annotations SET "rotationAngle" = degrees( ST_Azimuth( ST_StartPoint(NEW.the_geom), ST_EndPoint(NEW.the_geom) ) )-90 WHERE gid = NEW.gid;
RETURN NEW;
ELSIF TG_OP = 'UPDATE' THEN
UPDATE annotations SET "rotationAngle" = degrees( ST_Azimuth( ST_StartPoint(NEW.the_geom), ST_EndPoint(NEW.the_geom) ) )-90 WHERE gid = NEW.gid;
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
And when new row inserted in table or row edited i want to field rotationAngle setted with function result.
But when i inserting a new row in table function not work. I mean thath rotationAngle value not changed.
What can be wrong?
You are triggering an endless loop. Simplify the trigger function:
CREATE OR REPLACE FUNCTION set_angle()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
BEGIN
NEW."rotationAngle" := degrees(
ST_Azimuth(
ST_StartPoint(NEW.the_geom)
, ST_EndPoint(NEW.the_geom)
)
) - 90;
RETURN NEW;
END
$func$;
Assign to NEW directly. No WHERE in this case.
You must double-quote illegal column names. Better not to use such names to begin with.
Recent related answer.
Code for insert & upgrade is the same. I folded into one code path.
Use a BEFORE trigger. This way you can edit columns of the triggering row directly before they are saved:
CREATE TRIGGER set_angle
BEFORE INSERT OR UPDATE ON annotations
FOR EACH ROW EXECUTE PROCEDURE set_angle();
However
If you are just trying to persist a functionally dependent value in the table (and there are no other considerations): Don't. Use a view or a generated column instead:
Store common query as column?
Then you don't need any of this.
There are multiple things wrong here.
1) When you insert a row 'A' the function setAngle() is called. But in the function you are calling another update within the function which will trigger the function again, and again, and so on...To fix this don't issue a update! Just update the NEW records value independently and return it.

INSTEAD OF DELETE trigger (Postgresql)

I would like to disable the DELETE statement on a table.
What I need to do is a SET a field value instead of removing the respective record.
So far I have tried the following:
CREATE TRIGGER delete_trg
INSTEAD OF DELETE
ON schema.tbl
FOR EACH ROW
EXECUTE PROCEDURE schema.tbl_delete_fn();
My schema.tbl_delete_fn() function is as follows:
CREATE OR REPLACE FUNCTION schema.tbl_delete_fn()
RETURNS trigger AS
BEGIN
NEW.deleted := true;
RETURN NEW;
END;
So far this doesn't seem to work... any ideas?
You want a BEFORE DELETE trigger whose function returns NULL and the row variable is OLD, not NEW.
CREATE TRIGGER delete_trg
BEFORE DELETE
ON schema.tbl
FOR EACH ROW
EXECUTE PROCEDURE schema.tbl_delete_fn();
CREATE OR REPLACE FUNCTION schema.tbl_delete_fn()
RETURNS trigger AS '
BEGIN
UPDATE schema.tbl SET deleted=true WHERE ctid=OLD.ctid;
RETURN NULL;
END; ' language plpgsql;
Or...
CREATE RULE delete_rule
AS ON DELETE TO schema.tbl
DO INSTEAD NOTHING;
Pros: Clearer, no code is called for each row visited, and no SP required.
Cons: Less standard than the trigger solution.

PLS-00103, in Trigger

create or replace TRIGGER log_worlds BEFORE UPDATE OR INSERT ON worlds
DECLARE
ac VARCHAR2(50);
tab VARCHAR2(50);
world VARCHAR2(50);
BEGIN
IF UPDATING THEN
ac:='Aktualizacja';
END IF;
IF INSERTING THEN
ac:='Nowe';
END IF;
tab:='WORLDS';
world:='world_';
world:=world||cast(NEW_WORLD.NEXTVAL as VARCHAR2(10));
INSERT INTO log(ACTION_DATE,ACTION,TAB_NAME,ADDED_WORLD) VALUES(SYSDATE,ac,tab,world);
INSERT INTO worlds(WORLD_NAME) VALUES(world);
END;
Can someone help me with this,error information is about line 14? This trigger is supposed to add new values to the log table and to change the primary key value of the worlds table when my APEX application issues DML against the table.
the error you get suggests some syntax error in your code which I can't immediately find. However I would expect another error becuase Oracle won't allow you do execute DML statements (select, insert, update, delete) inside of a trigger on the same table as the trigger is on. Your trigger is on table worlds, so you are not allowed to insert a record into table worlds inside the trigger.
I think it's this line here:
world:=world||cast(NEW_WORLD.NEXTVAL as VARCHAR2(10));
If you replace it with:
world := world||to_char(NEW_WORLD.NEXTVAL);
it should work.
Btw: directly using a nextval call in an assignment does not work on versions prior to 11.x (not sure about the value of x here).
In 10.x you would need to declare a variable and then use:
SELECT to_char(new_world.nextval)
into world_num;
world := world || world_num;
The immediate syntax error is that assuming NEW_WORLD is a sequence you've created, you would need to do something like
SELECT world ||
cast( new_world.nextval as varchar2(10) )
INTO world
FROM dual;
rather than directly referencing the sequence in your CAST.
It's far from clear to me what your trigger is supposed to be doing though. It is, at a minimum, going to generate an infinite loop. Every INSERT on WORLDS is going to cause the trigger to fire which will generate an INSERT on WORLDS which will cause the trigger to fire, etc. Oracle will eventually raise an error when you exceed the maximum recursion depth. Perhaps you intended this to be a row-level trigger rather than a statement-level trigger that changed the value of :new.world_name? If that's the case, you'd probably want something like
create or replace TRIGGER log_worlds
BEFORE UPDATE OR INSERT ON worlds
FOR EACH ROW
DECLARE
ac VARCHAR2(50);
tab VARCHAR2(50);
world VARCHAR2(50);
BEGIN
IF UPDATING THEN
ac:='Aktualizacja';
END IF;
IF INSERTING THEN
ac:='Nowe';
END IF;
tab:='WORLDS';
world:='world_';
select world || cast(new_world.nextval as varchar2(10)
into world
from dual;
INSERT INTO log(ACTION_DATE,ACTION,TAB_NAME,ADDED_WORLD)
VALUES(SYSDATE,ac,tab,world);
:new.world_name := world;
END;
This assumes that world_name is not actually the primary key. If world_name is the primary key then it would make no sense to modify the primary key value when a row is updated-- you would only want to potentially assign the primary key if you are doing an INSERT.