I am trying to find the Date and time when QTY for Item A and Loc UK gets updated.
Can I have where clause in trigger??
CREATE TABLE tr_test (
item VARCHAR2 (10),
loc VARCHAR2 (10),
WHEN DATE,
qty );
insert into TR_TEST values ('A','UK',to_date('01/01/2013','MM/DD/YYYY',100);
insert into TR_TEST values ('B','US',to_date('01/01/2013','MM/DD/YYYY',200);
CREATE OR REPLACE TRIGGER "TEST_TRIGGER"
BEFORE UPDATE
ON tr_test
FOR EACH ROW
BEGIN
IF UPDATING ('QTY')
THEN
INSERT INTO tr_test_1
(item, loc, WHEN, qty)
SELECT :OLD.item, :OLD.loc, SYSDATE, :OLD.qty
FROM DUAL;
INSERT INTO tr_test_1
(item`enter code here`, loc, WHEN, oh)
SELECT :OLD.item, :OLD.loc, SYSDATE, :NEW.qty
FROM DUAL;
END IF;
END;
/
Related
PostgreSQL.
My trigger func:
CREATE OR REPLACE FUNCTION new_purchase() RETURNS TRIGGER AS $psql$
BEGIN
IF NOT EXISTS(
SELECT 1
FROM currents c
WHERE c.name = new.name
)
THEN
INSERT INTO currents (name, qty)
VALUES (new.name, new.qty);
ELSE
UPDATE currents
SET
qty = qty + new.qty
WHERE
name = new.name;
END IF;
return new;
END;
$psql$ language plpgsql;
Then my trigger:
create trigger new_purchase_trigger
after insert on purchases
for each statement
execute procedure new_purchase();
my two tables with which i am manipulating:
Purchases
CREATE TABLE purchases (
name VARCHAR,
date TIMESTAMP,
qty INTEGER,
price NUMERIC,
about VARCHAR
)
Currents
CREATE TABLE currents (
name VARCHAR,
qty INTEGER
)
My INSERT:
INSERT INTO purchases (name, date, qty, price, about)
VALUES ('cheese', '2022-07-31', 1000, 11500, 'holland');
And finally the error when i am adding data to purchases via INSERT INTO - VALUES:
ERROR: NULL value in column "name" of relation "currents" violates the NOT NULL constraint
DETAIL: Error string contains (null, null).
CONTEXT: SQL statement: "INSERT INTO currents (name, qty)
VALUES(new.name, new.qty)"
PL/pgSQL new_purchase() function, line 9, statement SQL statement
SQL state: 23502
You need a ROW level trigger for what you are trying to achieve. In a statement level trigger (for each statement) the record new is not populated:
create trigger new_purchase_trigger
after insert on purchases
for each row
execute procedure new_purchase();
Note that you can simplify your trigger function if you declare currents.name as the primary key:
CREATE OR REPLACE FUNCTION new_purchase()
RETURNS TRIGGER
AS $psql$
BEGIN
INSERT INTO currents (name, qty)
VALUES (new.name, new.qty)
on conflict (name) do update
set qty = currents.qty + excluded.qty;
return new;
END;
$psql$
language plpgsql;
I am trying to create a trigger to automatic generate and insert values with year and month (YYYYMM) into a table as inserts are made into another table.
Example:
As inserts are made into table 'original_table'
create table original_table
(opt_value char(2),
low_value varchar2(24),
high_value varchar2(24));
create table new_values
(id_values varchar2(24),
yr_month number(6));
Insert into original_table(opt_value,low_value,high_value) values ('EQ', '1111111111', '1111111111');
Insert into original_table(opt_value,low_value,high_value) values ('BT', '2222222000', '2222222999');
Insert into original_table(opt_value,low_value,high_value) values ('BT', '3333333350', '3333333399');
original_table
opt_value
low_value
high_value
EQ
1111111111
1111111111
BT
2222222000
2222222999
BT
3333333350
3333333399
Obs: Where EQ stands for 'equal' and BT 'between'. When 'EQ' just need to insert one of the values low or high don't matter, when 'BT' need to generate all the numbers between the two values and insert then into 'new_values' table.
table 'new_values' should get:
new_values
id_values
yr_month
1111111111
202111
2222222000
202111
2222222001
202111
2222222002
202111
...
...
2222222999
202111
3333333350
202111
3333333351
202111
...
...
3333333399
202111
I create a trigger which works, but i find it a little slow, and i don't like to use BEFORE INSERT statement, but can't use AFTER INSERT without getting mutating table error.
create or replace TRIGGER TRG_NAME
BEFORE INSERT
ON original_table
FOR EACH ROW
BEGIN
IF :NEW.opt_value = 'BT' THEN
INSERT INTO new_values (id_values, yr_month)
with tab123 (h_value, l_value, y_month)
as (select :NEW.high_value, cast(:NEW.low_value as number) , to_char(trunc(sysdate), 'YYYYMM')
from original_table
union all
select h_value, l_value +1, y_month
from tab123
where l_value < h_value)
select distinct l_value, y_month
from tab123;
ELSIF :NEW.opt_value = 'EQ' THEN
INSERT INTO new_values (id_values, yr_month) values
( :NEW.high_value, to_char(add_months(trunc(sysdate), -1), 'YYYYMM'));
END IF;
END;
Any tips in how to improve this code will be much appreciated.
You get a mutating table error, when you read from the table that is changing.
It's weird that you are getting it in an AFTER INSERT trigger and not with a BEFORE INSERT trigger. I would have assumed them to both result in the same error, because in both scenarios you read from the triggering table in your trigger. Well, this may have to do with only inserting one row. If you insert more rows at a time, you may get the error with both trigger variants.
In your case reading all the rows from the original_table in your trigger only produces duplicates anyway, that you fend off with DISTINCT. Instead, don't read from the table. It is not necessary. Read from DUAL instead, to get one row with the desired values:
with tab123 (h_value, l_value, y_month) as
(
select :new.high_value, cast(:new.low_value as number), to_char(sysdate, 'yyyymm')
from dual
union all
select h_value, l_value + 1, y_month
from tab123
where l_value < h_value
)
select l_value, y_month
from tab123;
This will get you rid of the mutating table error and also speed up the trigger.
I have 3 tables:
create table customer
(
customer_id integer primary key,
customer_first_name varchar2(50) not null,
customer_surrname varchar2(50) not null,
phone_number varchar2(15) not null,
customer_details varchar2(200) default 'There is no special notes'
);
create table place
(
table_number integer primary key,
table_details varchar2(200) default 'There is no details'
);
create table booking
(
booking_id integer primary key,
date_of_booking date,
number_of_persons number(2) not null,
customer_id integer not null,
foreign key(customer_id) references customer(customer_id),
table_number integer not null,
foreign key(table_number) references place(table_number)
);
I have to generate customer table using this kind of generator:
set SERVEROUTPUT on format wrapped;
set define off;
drop sequence customer_seq;
drop sequence place_seq;
--CUSTOMER TABLE INSERT ROW GENERATOR
create sequence customer_seq START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER customer_id_trigger
BEFORE INSERT ON customer
FOR EACH ROW
BEGIN
SELECT customer_seq.nextval INTO :new.customer_id FROM dual;
END;
/
DELETE FROM customer;
DECLARE
TYPE TABSTR IS TABLE OF VARCHAR2(250);
first_name TABSTR;
surrname TABSTR;
qname number(5);
phonenum number(15);
details TABSTR;
BEGIN
first_name := TABSTR ('Jhon','Poul','Jesica','Arnold','Max','Teemo','Tim','Mikel','Michael',
'Kristian','Adela','Mari','Anastasia','Robert','Jim','Juana','Adward',
'Jana','Ola','Kristine','Natali','Corey','Chester','Naomi','Chin-Chou');
surrname := TABSTR ('Grey','Brown','Robins','Chiho','Lee','Das','Edwins','Porter','Potter',
'Dali','Jordan','Jordison','Fox','Washington','Bal','Pitney','Komarowski',
'Banks','Albra','Shwiger');
details := TABSTR ('Exellent Customer','Good Customer','Always drunked','Left big tips',
'Bad Customer','Did not pay last bill','New Customer','VIP client');
qname := 100; — CHANGE THIS TO MANAGE HOW MANY ROWS YOU WANT TO BE ADDED
FOR i IN 1..qname LOOP
phonenum := dbms_random.value(111111111,999999999);
INSERT INTO customer VALUES (NULL, first_name(dbms_random.value(1,25)),
surrname(dbms_random.value(1,20)), phonenum, details(dbms_random.value(1,8)));
END LOOP;
DBMS_OUTPUT.put_line('Customers done!');
END;
/
--TABLE INSERT
DELETE FROM place;
create sequence place_seq start with 1 increment by 1;
insert into place values (place_seq.nextval, 'Near the window');
insert into place values (place_seq.nextval, default);
insert into place values (place_seq.nextval, 'Near the door');
insert into place values (place_seq.nextval, 'Near the window');
insert into place values (place_seq.nextval, 'Near the window');
insert into place values (place_seq.nextval, default);
insert into place values (place_seq.nextval, 'Near the door');
insert into place values (place_seq.nextval, 'Big table');
insert into place values (place_seq.nextval, default);
insert into place values (place_seq.nextval, 'Big table');
So the question is how can I insert client_id in "booking" table which have one of the numbers in "customers" table? Because every time I regenerate data in "Customers" table numbers are changing so I should somehow select numbers in an array and then randomly choose one of them from this array. The thing is I don't really know how to select from table to array. Can anybody help?
For PL/SQL version you can use BULK COLLECT and standard sys.odcinumberlist array.
create sequence booking_seq start with 1 increment by 1;
declare
customerIds sys.odcinumberlist;
placeIds sys.odcinumberlist;
number_of_generated_records number := 150; -- number of records to be generated
begin
-- fill the array of customer_id values
select customer_id
bulk collect into customerIds
from customer;
-- fill the array of place numbers
select table_number
bulk collect into placeIds
from place;
for i in 1..number_of_generated_records loop
insert into booking(booking_id,date_of_booking,number_of_persons,customer_id,table_number)
values(
booking_seq.nextval, -- booking_id
trunc(sysdate) + round(dbms_random.value(1,365)), -- date_of_booking
round(dbms_random.value(1,99)), -- number_of_persons
customerIds(round(dbms_random.value(1,customerIds.count))), -- customer_id
placeIds(round(dbms_random.value(1,placeIds.count))) -- table_number
);
end loop;
end;
But, for your case I would prefer pure sql:
insert into booking(booking_id,date_of_booking,number_of_persons,customer_id,table_number)
with
customer_subq as (
select customer_id, row_number() over (order by customer_id) rn from customer
),
place_subq as (
select table_number, row_number() over (order by table_number) rn from place
),
params as (
select 1500 number_of_generated_records,
(select count(1) from customer) customer_count,
(select count(1) from place) place_count
from dual
),
random_numbers as (
select round(dbms_random.value(1,1000)) random_number1,
round(dbms_random.value(1,1000)) random_number2,
round(dbms_random.value(1,1000)) random_number3,
round(dbms_random.value(1,1000)) random_number4
from dual,params
connect by level <= number_of_generated_records
)
select booking_seq.nextval booking_id,
trunc(sysdate) + mod(random_number1,365) date_of_booking,
mod(random_number1,100) number_of_persons,
customer_id,
table_number
from random_numbers,
params,
customer_subq,
place_subq
where mod(random_number1,customer_count) + 1 = customer_subq.rn
and mod(random_number2,place_count) + 1 = place_subq.rn
here's the value of ACCOUNT_NUMBER that has been generated by a sequence and inserted in ACCOUNTS table by ACCOUNT_NUMBER_TRIG trigger that i need to insert it into the TRANSACTION TABLE by the trigger ACCOUNTS_TRANSCATION_TRIG_1
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
DECLARE
V_ACC_NO ACCOUNTS.ACCOUNT_NUMBER%TYPE;
BEGIN
SELECT ACCOUNT_NO_SEQ.nextvaL INTO V_ACC_NO FROM DUAL;
:NEW.ACCOUNT_NUMBER := V_ACC_NO;
END ACCOUNT_NUMBER_TRIG;
------------------------------------------------------------------------------
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW DECLARE CURSOR ACCOUNTS_CUR IS
SELECT ACCOUNT_NUMBER FROM ACCOUNTS;
DECLARE
TEMP_1 NUMBER(5,0);
BEGIN
SELECT ACCOUNTS.ACCOUNT_NUMBER FROM INSERTED INTO TEMP_1
OPEN ACCOUNTS_CUR;
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- :NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
CLOSE ACCOUNTS_CUR;
END ACCOUNTS_TRANSCATION_TRIG_1;
CREATE TABLE accounts(
ACCOUNT_NUMBER number,
ACCOUNT_NAME varchar2(20)
);
CREATE SEQUENCE ACCOUNT_NO_SEQ;
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
BEGIN
:NEW.ACCOUNT_NUMBER :=ACCOUNT_NO_SEQ.nextvaL;
END ACCOUNT_NUMBER_TRIG;
/
CREATE TABLE transactions(
TR_DATE date,
TR_ACCOUNT_NUMBER number,
TR_TYPE varchar2(20),
TR_somenumber int
);
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW
BEGIN
INSERT INTO TRANSACTIONS( TR_DATE, TR_ACCOUNT_NUMBER, TR_TYPE, TR_somenumber )
VALUES
(
SYSDATE,
:NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
END ACCOUNTS_TRANSCATION_TRIG_1;
/
INSERT INTO accounts( ACCOUNT_NUMBER, ACCOUNT_NAME ) VALUES (1111,'My Name' );
select * from accounts;
ACCOUNT_NUMBER ACCOUNT_NAME
-------------- --------------------
2 My Name
select * from transactions;
TR_DATE TR_ACCOUNT_NUMBER TR_TYPE TR_SOMENUMBER
---------- ----------------- -------------------- -------------
2017/07/11 2 NEW ACCOUNT 0
You can use CURVAL to get the most recent value returned by NEXTVAL:
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW DECLARE CURSOR ACCOUNTS_CUR IS
BEGIN
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- ACCOUNT_NO_SEQ.curval,
'NEW ACCOUNT',
0
);
CLOSE ACCOUNTS_CUR;
END ACCOUNTS_TRANSCATION_TRIG_1;
However in this case there is no need, as it has been used to set the ACOUNT_NUMBER:
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- :NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
BTW unless you are on an old version of Oracle this should work for first trigger:
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
BEGIN
:NEW.ACCOUNT_NUMBER := ACCOUNT_NO_SEQ.nextvaL;
END ACCOUNT_NUMBER_TRIG;
(I suspect the WHEN clause is wrong - should be when is null?)
I have write a PL/SQL insert trigger but it doesn't compile properly but I don't see the problem.
create or replace TRIGGER trg_NEWROW
BEFORE INSERT or UPDATE OF STATUS_ID ON application FOR EACH ROW
BEGIN
INSERT INTO APPLICATION_HISTORY
(APPLICATION_ID, STATUS_ID, DATE_OF_CHANGE)
VALUES
(1, 1, SYSDATE)
END;
Try this: You missed a ';' at the end of Insert statement
CREATE OR REPLACE TRIGGER TRG_NEWROW
BEFORE INSERT OR UPDATE OF STATUS_ID
ON APPLICATION
FOR EACH ROW
BEGIN
INSERT INTO
APPLICATION_HISTORY ( APPLICATION_ID,
STATUS_ID,
DATE_OF_CHANGE )
VALUES
( 1,
1,
SYSDATE );
END;
/