trigger compilation error(insert) - triggers

this is my code:
create or replace trigger th
after insert on stock
for each row
declare
sqty number;
rl number;
isb number;
rq number;
begin
set sqty=(select stockqty from inserted);
set rl=(select reorderlevel from inserted);
set isb=(select isbn from inserted);
set rq=(select reorderquantity from inserted);
go
if sqty> rl then
delete from stock where isb=isbn;
insert into stock values(isb,sqty,rl,rq);
end if;
end;
questions:
1.if a after insert trigger is used it means all this happens after inserting right?what if i want to not insert a particular data what do i do?i mean like if weight<15 dont insert like that.
2.if i have inserted multiple data how to retrieve them?does a trigger get called for each of the insert?(if its an insert trigger).
3.this is giving me a compilation error,i just am not able to find the mistake,even using a cursor is giving me an error.
create or replace trigger t1
after insert on stock for each row
declare
cursor cl is select isbn,stockqty,reorderlevel,reorderquantity from stock where isbn>0;
begin
for c2 in c1 loop
if c2.stockqty>c2.reorderlevel then
delete from stock where isbn=c2.isbn;
insert into stock values(c2.isbn,c2.reorderquantity,c2.reorderlevel,c2.reorderquantity);
end if;
end loop;
end;
Btw i am using sql developer,weidly many of my trigger are not executing,but they are executing in oracle 8i.

I can't figure out what you are trying to do, but your syntax is all wrong (where did you get it from? SQL Server?). See documentation for the correct syntax.
You wrote:
set sqty=(select stockqty from inserted);
I suspect you want to do this:
sqty := :new.stockqty;
Ditto the next 3 lines.
Then you have:
go
which is nonsense. Just remove it.
Then you have:
if sqty> rl then
delete from stock where isb=isbn;
insert into stock values(isb,sqty,rl,rq);
end if;
Which appears to mean that if the inserted row's stockqty exceeds its reorderlevel then delete it and then insert it right back in again. This makes no sense, and cannot be done using a FOR EACH ROW trigger as you will get the "table is mutating" error.
Please explain what you are trying to achieve and then we can help see if it can be achieved.

Answers:
1. You can use a Check Constraint for that
2. I don't understand what you mean:
if you want to find duplicated records then you can group by all fields and use
having count(*) >0
but why use a trigger ?
or maybe you mean that you want the values within the trigger ? if so, use
:new and :old

Related

Function to Automatically set number of corresponding records in one table as default value in second table

I'm kind of stuck here. I'll try to keep it simple.
I have two tables.
Products (product_id, number_of_reviews, ...)
Reviews (main_product_id, review, ...)
main_product_id in Reviews table is the foreign key referencing product_id of Products table.
I need that the number_of_reviews column should automatically update to the number of reviews for that product_id present in the Reviews table. Match can be made by comparing product_id with main_product_id.
I know that I can use count to get number of reviews using this sql statement like:
SELECT COUNT(*) FROM reviews WHERE main_product_id = 'exampleid1'
Here exampleid1 should be product_id from products table.
But how do I create the function that I can call for DEFAULT in column number_of_reviews? Function that automatically takes the product_id from current row and passes it to that select statement and return the number of reviews...
I'm just so stuck here from hours, did a lot of searching but I can't figure it out.
It is my first time asking a question here on stackoverflow and my first time I'm taking interest in coding. PERN stack to be specific.
(I didn't like code for more than 6 years but now finally i built some interest)
First off this is actually a bad plan, you are saving a value the can easily be calculated. However, it seems quite common even though it often leads to complications. The function you need is a trigger; more specifically a trigger function and a trigger on reviews. (see demo)
create or replace function record_a_review_air()
returns trigger
language plpgsql
as $$
begin
update products
set reviews = reviews+1
where prod_id = new.prod_id;
return new;
end;
$$;
create trigger reviews_air
after insert
on reviews
for each row
execute function record_a_review_air();
NOTE: Setting a DEFAULT will not accomplish what you want. Doing so would set the a value when the Product is inserted. But would never be invoked again for that Product.
Here is what worked for me - Thanks to the demo and code provided by #belayer
create or replace function record_a_review_air()
returns trigger
language plpgsql
as $$
begin
update products
set n_reviews = (SELECT COUNT(*) FROM reviews WHERE main_prod_id = prod_id)
where prod_id = coalesce(new.main_prod_id, old.main_prod_id);
return new;
end;
$$;
create trigger reviews_air
after insert or delete or update of rev_id, main_prod_id, review
on reviews
for each row
execute function record_a_review_air();
(I updated the answer to add the new code that works for me on events(insert, update, delete) )

postgres TRIGGER upsert with returning

i want an upsert functionality that returns the (new/existing) id of the row.
Linking to my previous question. I asked previously about RULE postgres create rule on insert do nothing if exists insert otherwise; RETURNING id but looks like it is not possible.
So I resort to a trigger
CREATE OR REPLACE FUNCTION upsert_asset() RETURNS trigger AS $trigger_bound$
BEGIN
INSERT INTO asset(symbol, name, type, status)
VALUES (NEW.symbol, NEW.name, NEW.type, NEW.status)
ON CONFLICT (symbol) DO UPDATE SET symbol = EXCLUDED.symbol;
RETURN NEW;
END;
$trigger_bound$
LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER upsert_asset_trigger
AFTER INSERT ON asset
FOR EACH ROW
EXECUTE PROCEDURE upsert_asset();
I tested the above and works. So my questions are
Is this trigger a correct way to achieve this functionality? Any race conditions/performance issues that i should know about?
How can I generalize this query, by not giving the column names? asset(symbol, name, type, status). i do not want to pay attention to this rule every time I change my table. Is it possible to say NEW.* or column.* or something? What psuedorelations are available to achieve this? Please note there are some default columns too. So how does NEW.default_column get a value incase the insert statement has left that column in the insert statement?
Thanks,

PostgreSQL Trigger Copy New Row into other Table

I have a problem I am stuck on for some time now. So I wanted to reach out for a little help.
I have 2 tables which are holding the same data: transactions and transactions2.
I want to write a Trigger that is triggering every time a new row is added to transactions and insert it into transaction2 in PLSQL.
First I simply duplicated the table with
CREATE TABLE transactions2 (SELECT * FROM transactions WHERE 1=1);
I think I found out how to insert
CREATE OR REPLACE FUNCTION copyRow RETURNS TRIGGER AS $$
DECLARE
BEGIN
INSERT INTO transaction2
VALUES transaction;
END;
I think the syntax with this is also wrong, but how do I say, that the Trigger should start as soon as a new Insert into the first table is made?
Can anyone help me with this?
Thanks
Bobby
The correct syntax for an INSERT is INSERT (<column list>) VALUES (<values list>). The INSERT syntax isn't different in a function compared to "outside". So your trigger function should look something like:
CREATE OR REPLACE FUNCTION t2t2_f ()
RETURNS TRIGGER
AS
$$
BEGIN
INSERT INTO transactions2
(column_1,
...,
column_n)
VALUES (NEW.column_1,
...,
NEW.column_n);
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
Replace the column_is with the actual column names of your table. NEW is a pseudo record with which you can access the values of the new row.
To create the trigger itself use something like:
CREATE TRIGGER t2t2_t
AFTER INSERT
ON transactions
FOR EACH ROW
EXECUTE PROCEDURE t2t2_f();
You may want to use another timing, e.g. BEFORE instead of AFTER.
That should give you something to start with. Please consider studying the comprehensive PostgreSQL Manual for further and more detailed information.

PL/pgSQL trigger table entry limit

I'd like to get an opinion on a trigger I've written for a PostGreSQL Database in PL/pgSQL. I haven't done it previously and would like to get suggestions by more experienced users.
Task is simple enough:
Reduce the number of entries in a table to a set amount.
What should happen:
An INSERT into to the table device_position occurs,
If the amount of entries with a specific column (deviceid) value exceeds 50 delete the oldest.
Repeat
Please let me know if you see any obvious flaws:
CREATE OR REPLACE FUNCTION trim_device_positions() RETURNS trigger AS $trim_device_positions$
DECLARE
devicePositionCount int;
maxDevicePos CONSTANT int=50;
aDeviceId device_position.id%TYPE;
BEGIN
SELECT count(*) INTO devicePositionCount FROM device_position WHERE device_position.deviceid=NEW.deviceid;
IF devicePositionCount>maxDevicePos THEN
FOR aDeviceId IN SELECT id FROM device_position WHERE device_position.deviceid=NEW.deviceid ORDER BY device_position.id ASC LIMIT devicePositionCount-maxDevicePos LOOP
DELETE FROM device_position WHERE device_position.id=aDeviceId;
END LOOP;
END IF;
RETURN NULL;
END;
$trim_device_positions$ LANGUAGE plpgsql;
DROP TRIGGER trim_device_positions_trigger ON device_position;
CREATE TRIGGER trim_device_positions_trigger AFTER INSERT ON device_position FOR EACH ROW EXECUTE PROCEDURE trim_device_positions();
Thanks for any wisdom coming my way :)

postgres 9.3 database trigger creation

I am trying to create a trigger that on update of one table, runs a query and updates another table with the results.
Where I am getting stuck, is assigning the result of the query to a correctly typed variable.
The current error is that the array must start with "{" or other dimensional information however as I make tweaks I get other errors
Please see my current code below and let me know the best approach
Your help is very appreciated as I have spent a huge amount of time consulting google.
CREATE TYPE compfoo AS (ownership character varying (50), count INT);
CREATE OR REPLACE FUNCTION test1_update() RETURNS trigger AS
$$
DECLARE
largest_owner character varying (50);
temp_result compfoo[];
BEGIN
SELECT ownership, count(*) INTO temp_result
FROM austpoly2
WHERE ownership IS NOT NULL
group by ownership
ORDER BY count DESC
LIMIT 1;
largest_owner = temp_result[0].ownership;
UPDATE public.states
SET ownership= largest_owner
WHERE statecode='1';
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER test1_update_trigger
BEFORE UPDATE ON austpoly2
FOR EACH ROW EXECUTE PROCEDURE test1_update();
Thankyou a_horse with_no_name
Your response combined with
temp_result compfoo%ROWTYPE;
solved this problem