How to make and statement on IF trigger SQL? - sql

I have a trigger and I have conditions in it, and I wanted to make something like this, but I can't manage to do it, I tried several things but none of them worked...
IF NEW.foo = OLD.foo AND NEW.bar = OLD.bar
THEN
...
RETURN NEW
END IF;
END;
CREATE OR REPLACE FUNCTION user_histo() RETURNS trigger AS $$
BEGIN
IF TG_OP = 'INSERT'
THEN
INSERT INTO table (attribute1, attribute2, attribute3, attribute4, attribute5, attribute6, attribute7)
VALUES (NEW.attribute1, NEW.attribute2, NEW.attribute3, NEW.attribute4, NEW.attribute5, NEW.attribute6, 'INSERT');
RETURN NEW;
ELSIF TG_OP = 'UPDATE'
THEN
IF (OLD.attribute2 = NEW.attribute2 AND OLD.attribute3 = NEW.attribute3) // this is my problem
THEN
INSERT INTO table (attribute1, attribute2, attribute3, attribute4, attribute5, attribute6, attribute7)
VALUES (OLD.attribute1, OLD.attribute2, OLD.attribute3, OLD.attribute4, OLD.attribute5, OLD.attribute6, 'UPDATE');
INSERT INTO table (attribute1, attribute2, attribute3, attribute4, attribute5, attribute6, attribute7)
VALUES (NEW.attribute1, NEW.attribute2, NEW.attribute3, NEW.attribute4, NEW.attribute5, NEW.attribute6, 'INSERT');
RETURN NEW;
ELSE
RETURN NULL;
END IF;
END IF;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
CREATE TRIGGER trigger_table AFTER INSERT OR UPDATE ON table
FOR EACH ROW EXECUTE PROCEDURE user_histo();

Related

How can I call a function from another schema in Postgresql?

Tell me, how can I call a function from another schema in the trigger of an SQL function?
We have Postgresql, the main public schema and the amqp schema, which was formed when pg_amqp was connected.
The public schema has a trigger function for tracking INSERT/UPDATE/DELETE in the table:
if (TG_OP = 'INSERT') then
INSERT INTO table_1_audit_log (
table_1_id,
old_row_data,
new_row_data,
dml_type,
dml_timestamp
)
VALUES(
NEW.id,
null,
to_jsonb(NEW),
'INSERT',
CURRENT_TIMESTAMP
);
RETURN NEW;
elsif (TG_OP = 'UPDATE') then
INSERT INTO table_1_audit_log (
table_1_id,
old_row_data,
new_row_data,
dml_type,
dml_timestamp
)
VALUES(
NEW.id,
to_jsonb(OLD),
to_jsonb(NEW),
'UPDATE',
CURRENT_TIMESTAMP
);
RETURN NEW;
elsif (TG_OP = 'DELETE') then
INSERT INTO table_1_audit_log (
table_1_id,
old_row_data,
new_row_data,
dml_type,
dml_timestamp
)
VALUES(
OLD.id,
to_jsonb(OLD),
null,
'DELETE',
CURRENT_TIMESTAMP
);
RETURN OLD;
end if;
END;
I want to call the amqp.publish function from the amqp schema in this function:
SELECT amqp.publish(1, '', 'queu', 'messange')
But it gives me an error:
ERROR: function amqp.publish(integer, unknown, unknown, unknown) does not exist
It was necessary to set the types forcibly:
SELECT amqp.publish(1, ''::varchar, 'queu'::varchar, 'messange'::varchar)

Why is my 'select into a variable' statement making my stored procedure not insert anything into the table?

So I currently have a stored procedure that does a simple insert into a table called 'VALUE'. I tested this piece and called the procedure below:
Create Or Replace Procedure TEST_PROCEDURE(rValue_tx IN VARCHAR, rData_Type IN VARCHAR)
IS
BEGIN
IF rValue_Tx >= 0 THEN
IF rData_Type in ('TEST', 'REAL')
THEN
Insert into Value (VALUE_ID, VALUE_TX, CREATE_DT)
VALUES (1, rValue_tx, SYSDATE);
END IF;
END IF;
END TEST_PROCEDURE;
However; when i try to insert a number into a variable (adding another layer of complexity), nothing gets inserted. Below is code with another layer of complexity added to it:
Create Or Replace Procedure TEST_PROCEDURE_NEW(rValue_tx IN VARCHAR, rData_Type IN VARCHAR)
IS
v_MAX_historic_value value.value_tx%type;
BEGIN
---
SELECT MAX(BUFFER_MAX)
INTO v_MAX_Historic_Value
FROM max_look_up;
EXCEPTION
WHEN no_data_found
THEN SELECT 0
INTO v_MAX_Historic_Value
FROM DUAL;
---
IF rValue_Tx >= 0
THEN
IF rData_Type in ('TEST', 'REAL')
THEN
Insert into Value (VALUE_ID, VALUE_TX, CREATE_DT)
VALUES (null, rValue_tx, SYSDATE);
END IF;
END IF;
END TEST_PROCEDURE_NEW;
I am sure I am missing some fundamental rule of PL/SQL but can't seem to figure out which rule. It seems that it's the EXCEPTION that is causing the procedure not to insert any rows into the VALUE table, but I am not exactly sure why (or is there a better way to do this?)... any help would be greatly appreciated :(
Instead of using an EXCEPTION; is there a better way for me to handle when there is no data in max_look_up and nothing/null gets returned as a result?
With some proper code indenting it is obvious that all code except for the first SELECT statement is in the exception handler.
However, SELECT MAX(...) will always return data and never trigger the NO_DATA_FOUND exception. Therefore, the INSERT statement is never reached.
Create Or Replace Procedure TEST_PROCEDURE_NEW(rValue_tx IN VARCHAR, rData_Type IN VARCHAR)
IS
v_MAX_historic_value value.value_tx%type;
BEGIN
SELECT MAX(BUFFER_MAX)
INTO v_MAX_Historic_Value
FROM max_look_up;
EXCEPTION
WHEN no_data_found
THEN
SELECT 0
INTO v_MAX_Historic_Value
FROM DUAL;
IF rValue_Tx >= 0 THEN
IF rData_Type in ('TEST', 'REAL') THEN
Insert into Value (VALUE_ID, VALUE_TX, CREATE_DT)
VALUES (null, rValue_tx, SYSDATE);
END IF;
END IF;
END TEST_PROCEDURE_NEW;
What you probably want is to move the code out of the exception handler. You would do this by introducing another pair of BEGIN / END around the first SELECT statement and the exception handling code. However, since the exception is never triggered, it's easier to remove the exception handling altogether. Instead, you should check for NULL as MAX(...) returns NULL if no rows are selected.
Create Or Replace Procedure TEST_PROCEDURE_NEW(rValue_tx IN VARCHAR, rData_Type IN VARCHAR)
IS
v_MAX_historic_value value.value_tx%type;
BEGIN
SELECT MAX(BUFFER_MAX)
INTO v_MAX_Historic_Value
FROM max_look_up;
IF v_MAX_Historic_Value IS NULL THEN
v_MAX_Historic_Value := 0;
END IF;
...
IF rValue_Tx >= 0 THEN
IF rData_Type in ('TEST', 'REAL') THEN
Insert into Value (VALUE_ID, VALUE_TX, CREATE_DT)
VALUES (null, rValue_tx, SYSDATE);
END IF;
END IF;
END TEST_PROCEDURE_NEW;
I think you only misplaced Exception keyword, try this:
CREATE OR REPLACE PROCEDURE TEST_PROCEDURE_NEW (
RVALUE_TX IN VARCHAR,
RDATA_TYPE IN VARCHAR
)
IS
V_MAX_HISTORIC_VALUE VALUE.VALUE_TX%TYPE;
BEGIN
---
BEGIN
SELECT MAX (BUFFER_MAX) INTO V_MAX_HISTORIC_VALUE FROM MAX_LOOK_UP;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
SELECT 0 INTO V_MAX_HISTORIC_VALUE FROM DUAL;
END;
---
IF RVALUE_TX >= 0
THEN
IF RDATA_TYPE IN ('TEST', 'REAL')
THEN
INSERT INTO VALUE (VALUE_ID, VALUE_TX, CREATE_DT)
VALUES (NULL, RVALUE_TX, SYSDATE);
END IF;
END IF;
END TEST_PROCEDURE_NEW;
After some further research I found an alternate solution (without the use of exceptions) to my question (please correct me if I'm wrong):
Create Or Replace Procedure TEST_PROCEDURE_NEW(rValue_tx IN VARCHAR, rData_Type IN VARCHAR)
IS
v_MAX_historic_value value.value_tx%type;
BEGIN
with EXEC as
(select 0 buffer_max
from dual
UNION
select MAX(BUFFER_MAX)
FROM max_look_up) select max(buffer_max) into v_MAX_Historic_Value from EXEC;
IF rValue_Tx >= 0 THEN
IF rData_Type in ('TEST', 'REAL') THEN
Insert into Value (VALUE_ID, VALUE_TX, CREATE_DT)
VALUES (null, rValue_tx, SYSDATE);
END IF;
END IF;
END TEST_PROCEDURE_NEW;

how we send parameters to trigger function which is not related to affected table in PostgreSQL

CREATE TABLE emp (
empname text NOT NULL,
salary integer
);
CREATE TABLE emp_audit(
operation char(1) NOT NULL,
stamp timestamp NOT NULL,
userid text NOT NULL,
empname text NOT NULL,
salary integer
);
CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_audit$
BEGIN
--
-- Create a row in emp_audit to reflect the operation performed on emp,
-- make use of the special variable TG_OP to work out the operation.
--
IF (TG_OP = 'DELETE') THEN
INSERT INTO emp_audit SELECT 'D', now(), user_id, OLD.*;
RETURN OLD;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO emp_audit SELECT 'U', now(), user_id, OLD.*;
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO emp_audit SELECT 'I', now(), user_id, NEW.*;
RETURN NEW;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$emp_audit$ LANGUAGE plpgsql;
DROP TRIGGER emp_audit ON emp;
CREATE TRIGGER emp_audit
AFTER INSERT OR UPDATE OR DELETE ON emp
FOR EACH ROW EXECUTE PROCEDURE process_emp_audit();
user_id not in emp table
can we send parameters to trigger function which is not related to affected table in PostgreSQL. if I enter an active record of user there may some issue as if two different users active then trigger will confuse

How to create function that updates changes in one table from another table?

I have this function that I found here:
Insert trigger to Update another table using PostgreSQL
CREATE TABLE table1
(
id integer NOT NULL,
name character varying,
CONSTRAINT table1_pkey PRIMARY KEY (id)
)
CREATE TABLE table2
(
id integer NOT NULL,
name character varying
)
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO
table2(id,name)
VALUES(new.id,new.name);
RETURN new;
END;
$BODY$
language plpgsql;
CREATE TRIGGER trig_copy
AFTER INSERT ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
If I insert these two rows:
insert into table1 values (1, 'Andrey');
insert into table1 values (2, 'Mariza');
Then they also go into table2.
My problem is when I do an update on a value:
update table1 set name = 'Andi' where id = '1';
nothing happens in table2.
How can I create a function that updates changes in table?
To support UPDATE you can do this:
Trigger:
CREATE TRIGGER trig_copy
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
Function:
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
if TG_OP='INSERT' then
INSERT INTO table2(id,name) VALUES(new.id,new.name);
end if;
if TG_OP='UPDATE' then
Update table2 set name=new.name where id=old.id;
end if;
RETURN new;
END;
$BODY$
language plpgsql;
create or replace trigger trig_copy
after insert or update
on table1
begin
merge into table2 t2
using (
select
id,
name
from
table1 t1) t_new
on (t2.id = t_new.id)
when matched then
update
set
t2.name = t_new.name
where
t2.id = t_new.id
WHEN NOT MATCHED then
insert (id,name) values(t_new.id,t_new.name);
end;
This will be applied to all records, not only updated record.
If U want to update/insert only one record Create trigger
referencing new as new
for each row
and use :new.id :new.name , to find record You want in t2.
Thanks to all for help. This is the answer that had worked for me.
I took it from Elads and Justas solutions:
CREATE TRIGGER trig_copy
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
if TG_OP='INSERT' then
INSERT INTO table2
(SELECT * FROM table1
WHERE id NOT IN
(SELECT id FROM table2));
end if;
if TG_OP='UPDATE' then
Update table2 set name=new.name where id=old.id;
end if;
RETURN new;
end;
$BODY$
language plpgsql;

Oracle: Using subquery in a trigger

How can I work around the Oracle's limitation of not allowing subqueries in triggers.
Here's an example trigger I'm trying to create, but am unable to because I can't use a subquery.
CREATE OR REPLACE TRIGGER trigger_w_subquery
AFTER UPDATE OR INSERT ON project_archiving
FOR EACH ROW WHEN (old.archiving_status <> new.archiving_status
AND new.archiving_status = 1
AND (SELECT offer FROM projects WHERE projnum = :new.projnum) IS NULL
)
BEGIN
INSERT INTO offer_log (offer, status, date)
VALUES (null, 9, sysdate);
END;
This trigger would do it:
CREATE OR REPLACE TRIGGER trigger_w_subquery
AFTER UPDATE OR INSERT ON project_archiving
FOR EACH ROW WHEN (old.archiving_status <> new.archiving_status
AND new.archiving_status = 1
)
DECLARE
l_offer projects.offer%TYPE;
BEGIN
SELECT offer INTO l_offer
FROM projects
WHERE projnum = :new.projnum;
IF l_offer IS NULL THEN
INSERT INTO offer_log (offer, status, date)
VALUES (null, 9, sysdate);
END IF;
END;
I have assumed that the select from projects will always find a row; if not it will raise a NO_DATA_FOUND exception that you may need to handle.
I expect that you want something like
CREATE OR REPLACE TRIGGER trigger_w_subquery
AFTER UPDATE OR INSERT ON project_archiving
FOR EACH ROW
WHEN (old.archiving_status <> new.archiving_status
AND new.archiving_status = 1)
DECLARE
l_offer projects.offer%TYPE;
BEGIN
SELECT offer
INTO l_offer
FROM projects
WHERE projnum = :new.projnum;
IF( l_offer IS NULL )
THEN
INSERT INTO offer_log (offer, status, date)
VALUES (null, 9, sysdate);
END IF;
END;
Can you put the condition into the action (between BEGIN and END) instead of in the 'whether it fires'? Yes, it means that the trigger body might be fired more often - but if it gets you around the problem...