trigger question in Oracle 18C, 'declare the content' - sql

Hi I do have some problems when I try to create triggers.
`CREATE OR REPLACE TRIGGER summary_check
BEFORE UPDATE OR INSERT ON POST
FOR EACH ROW
BEGIN
IF : NEW.summary != SUBSTR(content,1,11) || '...' THEN
RAISE_APPLICATION_ERROR(-20001,'The summary rule need to be followed');
END IF;
END;`
This one have a compiled problem which I need declare content, then if I use post.content, it tell me that this kind of context is not allowrd? both them are VARVHAR so they are the same type. not user what I should of change here .
`CREATE OR REPLACE TRIGGER date_check
BEFORE UPDATE OR INSERT ON Likes
FOR EACH ROW
DECLARE
V_DATE DATE;
BEGIN
SELECT created_on
INTO V_DATE
FROM post
WHERE post_id = post.post_id;
IF : NEW.liked_on < v_date THEN
RAISE_APPLICATION_ERROR(-20001,'the date have to entered correctly ');
END IF;
END;`
And here is another one, this one has no problem with compile, but when I try to test the trigger, it always say that the exact fetch returns more than requested, not sure how to I change it?

This query:
SELECT created_on
INTO V_DATE
FROM post
WHERE post_id = post.post_id;
Is not doing what you intend. The post_id also refers to post.post_id. So this returns all rows in post where post_id is not NULL.
I think you want:
SELECT p.created_on
INTO V_DATE
FROM post p
WHERE :NEW.post_id = p.post_id;

For your first problem, I think you don't even need trigger if both the columns used in the trigger belongs to the same table.
It can be done via adding CHECK constraint.
Alter table post
Add constraint summary_check
Check (summary = SUBSTR(content,1,11) || '...');
For the second problem, as mentioned by #gordon, just use :new.post_id instead of post.post_id.
Cheers!!

Related

Count(*) not working properly

I create the trigger A1 so that an article with a certain type, that is 'Bert' cannot be added more than once and it can have only 1 in the stock.
However, although i create the trigger, i can still add an article with the type 'Bert'. Somehow, the count returns '0' but when i run the same sql statement, it returns the correct number. It also starts counting properly if I drop the trigger and re-add it. Any ideas what might be going wrong?
TRIGGER A1 BEFORE INSERT ON mytable
FOR EACH ROW
DECLARE
l_count NUMBER;
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
SELECT COUNT(*) INTO l_count FROM mytable WHERE article = :new.article;
dbms_output.put_line('Count: ' || l_count);
IF l_count >0 THEN
IF(:new.TYPEB = 'Bert') THEN
dbms_output.put_line('article already exists!');
ROLLBACK;
END IF;
ELSIF (:new.TYPEB = 'Bert' AND :new.stock_count>1) THEN
dbms_output.put_line('stock cannot have more than 1 of this article with type Bert');
ROLLBACK;
END IF;
END;
This is the insert statement I use:
INSERT INTO mytable VALUES('Chip',1,9,1,'Bert');
A couple of points. First, you are misusing the autonomous transaction pragma. It is meant for separate transactions you need to commit or rollback independently of the main transaction. You are using it to rollback the main transaction -- and you never commit if there is no error.
And those "unforeseen consequences" someone mentioned? One of them is that your count always returns 0. So remove the pragma both because it is being misused and so the count will return a proper value.
Another thing is don't have commits or rollbacks within triggers. Raise an error and let the controlling code do what it needs to do. I know the rollbacks were because of the pragma. Just don't forget to remove them when you remove the pragma.
The following trigger works for me:
CREATE OR REPLACE TRIGGER trg_mytable_biu
BEFORE INSERT OR UPDATE ON mytable
FOR EACH ROW
WHEN (NEW.TYPEB = 'Bert') -- Don't even execute unless this is Bert
DECLARE
L_COUNT NUMBER;
BEGIN
SELECT COUNT(*) INTO L_COUNT
FROM MYTABLE
WHERE ARTICLE = :NEW.ARTICLE
AND TYPEB = :NEW.TYPEB;
IF L_COUNT > 0 THEN
RAISE_APPLICATION_ERROR( -20001, 'Bert already exists!' );
ELSIF :NEW.STOCK_COUNT > 1 THEN
RAISE_APPLICATION_ERROR( -20001, 'Can''t insert more than one Bert!' );
END IF;
END;
However, it's not a good idea for a trigger on a table to separately access that table. Usually the system won't even allow it -- this trigger won't execute at all if changed to "after". If it is allowed to execute, one can never be sure of the results obtained -- as you already found out. Actually, I'm a little surprised the trigger above works. I would feel uneasy using it in a real database.
The best option when a trigger must access the target table is to hide the table behind a view and write an "instead of" trigger on the view. That trigger can access the table all it wants.
You need to do an AFTER trigger, not a BEFORE trigger. Doing a count(*) "BEFORE" the insert occurs results in zero rows because the data hasn't been inserted yet.

SQL - Trigger "not in"

I'm trying to make a time trigger in the dubbing database. I want to check that character can't be dubbed in the dubbing of the movie in which the character doesn't appear. Here's the PDM:
and CDM
I'm the begginer with SQL but I know that there should be some trigger in the table 'DUBBES'. I tried to make something like this, but i got a message that trigger is invalid:
CREATE OR REPLACE TRIGGER x_character
BEFORE INSERT OR UPDATE ON dubbes FOR EACH ROW
DECLARE
IF ( :NEW.CHAR_id_character NOT IN ( SELECT CHAR_id_CHARACTER
FROM APPEARS
WHERE APPEARS.MOV_id_movie = (SELECT dubbing.Mov_id_movie
FROM DUBBING
WHERE dubbing.id_dubbing = :NEW.dab_id_dubing)))
THEN
RAISE_APPLICATION_ERROR(-20000, 'Character is not in this movie.');
END IF;
END;
/
I would really appreciate any help.
Many thanks in advance!
I think that your code should be like this:
create or replace trigger X_character BEFORE INSERT OR UPDATE ON dubbes
FOR EACH ROW
DECLARE
haveit number;
idmovie number;
begin
select dubbing.Mov_id_movie into idmovie from DUBBING where dubbing.id_dubbing = :new.dab_id_dubing;
select count(*) into haveit from APPEARS
where
APPEARS.MOV_id_movie = idmovie and
APPEARS.CHAR_id_CHARACTER = :new.CHAR_id_character;
IF( haveit = 0 ) then
RAISE_APPLICATION_ERROR(-20000, 'Character is not in this movie.');
END IF;
END;
/
From your diagram, you don't need a trigger for this. If you redefine your FK_DUBBES_DUBBES3_CHARACTE foreign key to refer to APPEARS.CHAR_ID instead of directly against CHARACTER.ID_CHARACTER then the requirement will be enforced for you, without the additional overhead of a trigger.
(As an aside, you might find it easier to have more consistent column names, and simpler key names...)
Begin keyword is missing after DECLARE

PostgreSQL 1 to many trigger procedure

I wrote this query in PostgreSQL:
CREATE OR REPLACE FUNCTION pippo() RETURNS TRIGGER AS $$
BEGIN
CHECK (NOT EXISTS (SELECT * FROM padre WHERE cod_fis NOT IN (SELECT padre FROM paternita)));
END;
$$ LANGUAGE plpgsql;
It returns:
Syntax error at or near CHECK.
I wrote this code because I have to realize a 1..n link between two tables.
You can't use CHECK here. CHECK is for table and column constraints.
Two further notes:
If this is supposed to be a statement level constraint trigger, I'm guessing you're actually looking for IF ... THEN RAISE EXCEPTION 'message'; END IF;
(If not, you may want to expand and clarify what you're trying to do.)
The function should return NEW, OLD or NULL.

Postgres: checking value before conditionally running an update or delete

I've got a fairly simple table which stores the records' authors in a text field as shown here:
CREATE TABLE "public"."test_tbl" (
"index" SERIAL,
"testdate" DATE,
"pfr_author" TEXT DEFAULT "current_user"(),
CONSTRAINT "test_tbl_pkey" PRIMARY KEY("index");
The user will never see the index or pfr_author fields, but I'd like them to be able to UPDATE the testdate field or DELETE whole records if they have permission and if they are the author. i.e. if test_tbl.pfr_author = CURRENT_USER THEN permit the UPDATE OR DELETE, but if not then raise an error message such as "Sorry, you do not have permission to edit this record.".
I have not gone down the route of using a trigger as I figure that even if it is executed before row update the user-requested update will still take place afterwards regardless.
I've tried doing this through a rule, but end up with infinite recursion as I put an update command inside the rule. Is there some way to do this using rules alone or a combination of a rule and trigger?
Thanks very much for any help!
Use a row level BEFORE trigger on UPDATE and DELETE to do this. Just have it return NULL when the operation is not permitted and the operation will be skipped.
http://www.postgresql.org/docs/9.0/interactive/trigger-definition.html
the trigger function have some problem,resulting recursive loop update.You should do like this:
CREATE OR REPLACE FUNCTION "public"."test_tbl_trig_func" () RETURNS trigger AS $body$
BEGIN
IF not (old.pfr_author = "current_user"() OR "current_user"() = 'postgres') THEN
NULL;
END IF;
RETURN new;
END;
$body$ LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER COST 100;
I have a test like this,it does well;
UPDATE test_tbl SET testdate = CURRENT_DATE WHERE test_tbl."index" = 2;

Create a trigger that updates a column on one table when a column in another table is updated

i have two tables
Order(id, date, note)
and
Delivery(Id, Note, Date)
I want to create a trigger that updates the date in Delivery when the date is updated in Order.
I was thinking to do something like
CREATE OR REPLACE TRIGGER your_trigger_name
BEFORE UPDATE
ON Order
DECLARE
BEGIN
UPDATE Delivery set date = ??? where id = ???
END;
How do I get the date and row id?
thanks
How do i get the date and row id?
Assuming these are columns on your ORDER table called DELIVERY_DATE and ID your trigger should look something like this:
CREATE OR REPLACE TRIGGER your_trigger_name
BEFORE UPDATE ON Order
FOR EACH ROW
BEGIN
if :new.delivery_date != :old.delivery_date
then
UPDATE Delivery d
set d.delivery_date = :new.delivery_date
where d.order_id = :new.id;
end if;
END;
Note the FOR EACH ROW clause: that is necessary to reference values from individual rows. I have used an IF construct to test whether to execute the UPDATE on Delivery. If you have no other logic in your trigger you could write it like this...
CREATE OR REPLACE TRIGGER your_trigger_name
BEFORE UPDATE OF delivery_date ON Order
FOR EACH ROW
BEGIN
UPDATE Delivery d
set d.delivery_date = :new.delivery_date
where d.order_id = :new.id;
END;
I have answered the question you asked but, as an aside, I will point out that your data model is sub-optimal. A properly normalized design would hold DELIVERY_DATE on only one table: DELIVERY seems teh logical place for it.
Use the OLD and NEW bind variables. OLD references the row or column being updated before the change is made; NEW references it after the change.
CREATE OR REPLACE TRIGGER trig1
BEFORE UPDATE
ON order REFERENCING NEW AS new
FOR EACH ROW
BEGIN
UPDATE delivery
SET ddate = :new.ddate
WHERE id = :new.id;
END;
You can modify the REFERENCING clause to give your bind variables different names. You can include OLD as <name> too. Example:
CREATE OR REPLACE TRIGGER trig1
BEFORE UPDATE
ON order REFERENCING OLD AS old_values NEW AS new_values
...
If you don't want to change the default names of "old" and "new", you can leave out the REFERENCING clause completely.
There is an implicit new and old reference in the trigger in the form of:
REFERENCING OLD AS OLD NEW AS NEW
You can write to the :NEW value but not to the :OLD value.
UPDATE Delivery set date = :new.delivery_date where id = :new.id;
CREATE OR REPLACE TRIGGER "BUR_TABLENAME" BEFORE
UPDATE ON "TABLE" FOR EACH ROW
BEGIN
If :new.active_date is not null Then
:new.active_date := TRUNC(:new.active_date);
End If;
END;
Template:
CREATE OR REPLACE TRIGGER TRIGGER_NAME
BEFORE
UPDATE
ON TABLE_NAME
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
V_VARIABLE NUMBER (1);
BEGIN
//Do Stuff;
null;
end;
Whenever there is a need for this kind of trigger, have a good look at your design. Is there really a need for a separate delivery record? Does an order really have more than 1 delivery ?
Triggers seem nice but they do tend to mess things up pretty quickly.