How to select max value from oracle sql table column and make a trigger using that value - oracle-sqldeveloper

I have a trigger as follows
create or replace TRIGGER MY_TRIGGER
BEFORE INSERT ON MY_TABLE
FOR EACH ROW
BEGIN
:new.ID := max(ID)+100;
END;
Whenever I insert a new row into the table I want the new ID(a column in MY_TABLE) to be 100 more than the maximum value of ID already existing in the table. I cannot use max(ID) inside the trigger. Can someone tell what can be used instead of that to pick out the max value

Related

Yii2 PostgreSql how to add default value from another column on addColumn in migration

I have PostgreSQL DB table with 4 columns ex. col1, col2, col3, col4.
Col3 is unique indexed. I want to add new column with notNull and default value of col3 as after that I want to make newly created column also unique?
Is it possible to achieve this by simple Yii2 migration?
You cannot get what you want directly; the default expression cannot reference a another column. What you can do is make the new column unique but null-able, then update the values, and finally make the column not null. If needed you can create a trigger to maintain the new column. (see demo)
alter table a_table add new_col text unique;
update a_table set new_col = col3;
-- clean up any null values in new_col; (there is nothing saying
alter table a_table alter column new_col set not null;
If need long term the create a trigger function and trigger to manage new_col:
-- set new_col column
create or replace function new_col()
returns trigger
language plpgsql
as $$
begin
new.new_col = coalesce(new.new_col, new.col3, old.new_col, ''); -- always on update ???
return new;
end;
$$;
create trigger a_table_biur
before insert or update
on a_table
for each row execute function new_col();
The trigger function used the minimum logic necessary, and will not satisfy every condition. You will need to evaluate that.

Postgres statement-level trigger ERROR: transition tables cannot be specified for triggers with column lists

I have a table my_table with columns id (int), value (varchar), and modified (timestamp). I want to update the timestamp by the trigger each time I set a new value. I'm changing thousands of rows in one query so I do not want to use the row-level trigger (aka FOR EACH ROW) and modify the whole updated set in one invocation and not the thousands for each row. This is the trigger I'm trying to create:
CREATE TRIGGER my_trigger
AFTER UPDATE OF value
ON my_schema.my_table
REFERENCING NEW TABLE AS updated OLD TABLE AS prev
FOR EACH STATEMENT
EXECUTE PROCEDURE my_schema.my_trigger_function()
I'm getting an error during the creation:
[0A000] ERROR: transition tables cannot be specified for triggers with column lists
How can I access the updated data set if I remove the "REFERENCING NEW TABLE AS updated OLD TABLE AS prev" and won't be able to query and join on updated or prev transition tables? If I remove the "OF value" part I'd get the recursive trigger invocation since the trigger fires itself again by changing the same table but a different column. So the best solution I've come up with it's to eliminate the second recursive invocation by the IF condition:
CREATE TRIGGER my_trigger
AFTER UPDATE
ON my_schema.my_table
REFERENCING NEW TABLE AS updated OLD TABLE AS prev
FOR EACH STATEMENT
EXECUTE PROCEDURE my_schema.my_trigger_function()
CREATE OR REPLACE FUNCTION my_schema.my_trigger_function()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
IF EXISTS(SELECT 1
FROM updated
INNER JOIN prev ON updated.modified = prev.modified) THEN
UPDATE my_schema.my_table
SET modified = NOW()
WHERE id IN (SELECT id FROM updated);
END IF;
RETURN NULL;
END;
$$;
If I knew how to access all updated rows with "AFTER UPDATE OF value" without updated and prev I'd make it better, that's why I'm here.
You could avoid recursive invocation of your trigger by adding WHEN (PG_TRIGGER_DEPTH() = 0) to the declaration:
CREATE TRIGGER my_trigger
AFTER UPDATE ON my_schema.my_table
REFERENCING NEW TABLE AS updated OLD TABLE AS prev
FOR EACH STATEMENT
WHEN (PG_TRIGGER_DEPTH() = 0)
EXECUTE PROCEDURE my_schema.my_trigger_function()

Alter PostreSQL column into a GENERATED ALWAYS column

I have an already made table:
cotizacion(idCot(PK), unit_price,unit_price_taxes)
I need to convert unit_price_taxes into a generated column that is equal to unit_price*1.16. The issue is I can't find the alter table statement which will give me this. Dropping table and creating it again is not an option as this table is already deeply linked with the rest of the database and reinserting all records is not an option at this point.
I tried the following:
ALTER TABLE cotizacion
alter column unit_price_taxes set
GENERATED ALWAYS AS (unit_price*1.16) STORED;
But it's not working. Does anybody know how to get this done or if it's even possible? I would like to avoid creating a new column.
Thanks!
**EDIT:
I also tried the following trigger implementation:
CREATE OR REPLACE FUNCTION calculate_price_taxes()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
declare pu money;
begin
select unit_price from cotizacion into pu
where idCot = new."idCot";
update cotizacion
set unit_price_taxes = pu * (1.16)
where idCot = new."idCot";
return new;
end;
$function$
;
And the trigger delcaration:
Create or replace trigger price_taxes
after update on cotizacion
for each row
execute procedure
calculate_price_taxes()
The most probable reason for your trigger to go into an infinite recursion is that you are running an UPDATE statement inside the trigger - which is the wrong thing to do. Create a before trigger and assign the calculated value to the new record:
create trigger update_tax()
returns trigger
as
$$
begin
new.unit_price_taxes := unit_price * 1.16;
return new;
end;
$$
language plpgsql;
create trigger update_tax_trigger()
before update or insert on cotizacion
for each row execute procedure update_tax();
The only way to "convert" that column to a generated one, is to drop it and add it again:
alter table cotizacion
drop unit_price_taxes;
alter table cotizacion
add unit_price_taxes numeric generated always as (unit_price*1.16) stored;
Note that this will rewrite the entire table which will block access to it. Adding the trigger will be less invasive.

Get data of multiple inserted rows in one object using trigger in postgres

I am trying to write a trigger which gets data from the table attribute in which multiple rows are inserted corresponding to one actionId at one time and group all that data into the one object:
Table Schema
actionId
key
value
I am firing trigger on rows insertion,SO how can I handle this multiple row insertion and how can I collect all the data.
CREATE TRIGGER attribute_changes
AFTER INSERT
ON attributes
FOR EACH ROW
EXECUTE PROCEDURE log_attribute_changes();
and the function,
CREATE OR REPLACE FUNCTION wflowr222.log_task_extendedattribute_changes()
RETURNS trigger AS
$BODY$
DECLARE
_message json;
_extendedAttributes jsonb;
BEGIN
SELECT json_agg(tmp)
INTO _extendedAttributes
FROM (
-- your subquery goes here, for example:
SELECT attributes.key, attributes.value
FROM attributes
WHERE attributes.actionId=NEW.actionId
) tmp;
_message :=json_build_object('actionId',NEW.actionId,'extendedAttributes',_extendedAttributes);
INSERT INTO wflowr222.irisevents(message)
VALUES(_message );
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
and data format is,
actionId key value
2 flag true
2 image http:test.com/image
2 status New
I tried to do it via Insert trigger, but it is firing on each row inserted.
If anyone has any idea about this?
I expect that the problem is that you're using a FOR EACH ROW trigger; what you likely want is a FOR EACH STATEMENT trigger - ie. which only fires once for your multi-line INSERT statement. See the description at https://www.postgresql.org/docs/current/sql-createtrigger.html for a more through explanation.
AFAICT, you will also need to add REFERENCING NEW TABLE AS NEW in this mode to make the NEW reference available to the trigger function. So your CREATE TRIGGER syntax would need to be:
CREATE TRIGGER attribute_changes
AFTER INSERT
ON attributes
REFERENCING NEW TABLE AS NEW
FOR EACH STATEMENT
EXECUTE PROCEDURE log_attribute_changes();
I've read elsewhere that the required REFERENCING NEW TABLE ... syntax is only supported in PostgreSQL 10 and later.
Considering the version of postgres you have, and therefore keeping in mind that you can't use a trigger defined FOR EACH STATEMENT for your purpose, the only alternative I see is
using a trigger after insert in order to collect some information about changes in a utility table
using a unix cron that execute a pl/sql that do the job on data set
For example:
Your utility table
CREATE TABLE utility (
actionid integer,
createtime timestamp
);
You can define a trigger FOR EACH ROW with a body that do something like this
INSERT INTO utilty values(NEW.actionid, curent_timestamp);
And, finally, have a crontab UNIX that execute a file or a procedure that to something like this:
SELECT a.* FROM utility u JOIN yourtable a ON a.actionid = u.actionid WHERE u.createtime < current_timestamp;
// do something here with records selected above
TRUNCATE table utility;
If you had postgres 9.5 you could have used pg_cron instead of unix cron...

How to create a Trigger in PostgreSql?

TRIGEER-->To get a column value from one table to other table when i insert values?
I am having two tables(customer_details and loan_balance).
What i need is, I must get the column (custid)of customer_details table to the loan_balance table when i insert the data into the loan_balance table.
This is the full set up of my query : SQL FIDDLE
So i need a trigger to be raised and the data should be updated automatically without dynamic insertion of custid.
Postgres has an unconventional way of creating triggers:
create a function that returns type trigger and return the NEW row record
create a trigger that executes the function
Here's the code you need:
CREATE FUNCTION synch_custid_proc()
RETURNS trigger AS $$
BEGIN
NEW.custid = (
select max(custid)
from customer_details
where creditid = NEW.creditid
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql
CREATE TRIGGER synch_custid_trig
BEFORE INSERT ON loan_amount
FOR EACH ROW
EXECUTE PROCEDURE synch_custid_proc();
I chosen to select max(custid) rather than simply custid when finding the value in case there are multiple rows that match. You might have to adjust this logic to suit your data.
See a live demo on SQLFiddle