Essentially I wish to create a trigger that keeps track and edits the date_created column of a specific row after every insert or update.
These are the columns in my table:
| customer_id | store_id | Quantity | date_created |
the customer_id and store_id together are the primary key of the table
What I have so far:
CREATE OR REPLACE TRIGGER date_trig
BEFORE INSERT ON customer_table
FOR EACH ROW
DECLARE
BEGIN
-- This is where I assume the date will be set or edited
END;
I am brand new to PL/SQL so I am struggling with the actual body of this trigger.
Also, do I have the structure of a trigger correctly formed?
Hi Please find sample code.
create or replace trigger emp_mod_date
before update or insert on emp
for each row
begin
:new.mdate := sysdate;
end;
Use DEFAULT SYSDATE on the colum date_created like already suggested
if you insist to use a trigger, just write :NEW.date_created := SYSDATE;
Related
I want to create something that will update my data in table automaticly but i don't know what to use.
i have nr1 table
create table NR1
(
id INTEGER not null,
price INTEGER,
price2 INTEGER,
start_date DATE,
end_date DATE,
duration NUMBER
)
and i tried to create trigger which will update always after inserting or updateing my table, end_date, so i tried something like this:
create or replace trigger update_nr1_date
after update or insert on nr1
for each row
when (new.id>0)
declare
begin
UPDATE nr1
set nr1.end_date =add_months(nr1.start_date, nr1.duration);
end;
but it have mutation problems, and i read something about that, and i udersatnd the concept of this. I want to make trigger like this (not sheduler, because i want to get it automaticly after inserting or updating some rows). Is it possible while inserting data to table it itself refill missing columns?
Your trigger needs to before update in order to change the value in the row the trigger is fired again; and it needs to modify the current row's (new) pseudorecord via an assignment statement - you should not be issuing a separate update statement inside the trigger. It will cascade, for a start, and in your example woudl try to update every row in the table. But that is also causing the mutating table error - you're trying to update rows in the table the trigger is against. There are workarounds when that is actually necessary, but it isn't here, that update should just not be there.
So you would do:
create or replace trigger update_nr1_date
before update or insert on nr1
for each row
when (new.id>0)
begin
:new.end_date := add_months(:new.start_date, :new.duration);
end;
/
But if you're on 11g or higher you can use a virtual column instead, without needing a trigger at all:
create table NR1
(
id INTEGER not null,
price INTEGER,
price2 INTEGER,
start_date DATE,
end_date DATE generated always as (add_months(start_date, duration)) virtual,
duration NUMBER
)
Then when you insert, skip that column in the statement:
insert into nr1 (id, price, price2, start_date, duration)
values (1, 2, 3, date '2018-06-01', 3);
1 row inserted.
select * from nr1 where id = 1;
ID PRICE PRICE2 START_DATE END_DATE DURATION
---------- ---------- ---------- ---------- ---------- ----------
1 2 3 2018-06-01 2018-09-01 3
The end date always reflects the values of the other two columns.
I'm trying to create a trigger but I have learned I can not design it as in my first attempt, which I'm showing below. This will cause a 'mutating table' error due to selecting from the table as it is being modified. It actually didn't cause this error when inserting only one record at a time, but when I insert multiple records at once it does.
The purpose of the trigger is to count the number of records in the table where the customer is equal to the customer about to be inserted, and to set the new order_num value as count+1. I also have a public key value set by the trigger which draws from a sequence. This part works ok once I remove the order_num part of the trigger and the associated SELECT. How can I achieve what I am trying to do here? Thanks in advance.
CREATE OR REPLACE TRIGGER t_trg
BEFORE INSERT ON t
FOR EACH ROW
DECLARE
rec_count NUMBER(2,0);
BEGIN
SELECT COUNT(*) INTO rec_count
FROM t
WHERE customer_id = :NEW.customer_id;
:NEW.order_num:= rec_count+1;
:NEW.order_pk_id:= table_seq.NEXTVAL;
END;
Two triggers and temp table approach can provide solution to you seek, preventing mutating table error. However performance will most likely suffer.
create global temporary table cust_temp(customer_id number, cust_cnt number);
create or replace trigger t_trig1
before insert on t
declare
begin
insert into cust_temp select customer_id, count(*) from t group by customer_id;
end;
/
CREATE OR REPLACE TRIGGER t_trg2
BEFORE INSERT ON t
FOR EACH ROW
DECLARE
rec_count number;
BEGIN
BEGIN
SELECT cust_cnt INTO rec_count
FROM cust_temp
WHERE customer_id = :NEW.customer_id;
EXCEPTION when no_data_found then rec_count := 0;
END;
:NEW.order_num:= rec_count+1;
:NEW.order_pk_id:= table_seq.NEXTVAL;
update cust_temp set cust_cnt = rec_count + 1
where customer_id = :NEW.customer_id;
END;
/
I have the following table called employees
create table employees
(
eno number(4) not null primary key,
ename varchar2(30),
zip number(5) references zipcodes,
hdate date
);
And I'm trying to set a trigger on the table that will fire before an update or delete that will check the system time first to see if the time is between 12:00-13:00, if it is it will allow the insertion, otherwise prevent it.
My guess is so far:
CREATE or REPLACE TRIGGER twelve_one
BEFORE INSERT or Update ON employees
BEGIN
IF
....
ELSE
....
END;
But that's how far I've gotten into unfortunately. Can someone please help me to retrieve the system time first? Then how can I set up that IF ELSE block? And finally How to abort the transaction/insertion/update?
I'm using Oracle SQL Developer 4.02.15
Many Thanks
I assume you must declare temporary variable to set time now, and then compare the temporary variable.
CREATE OR REPLACE TRIGGER TWELVE_ONE
BEFORE INSERT OR UPDATE
ON EMPLOYEES
FOR EACH ROW
DECLARE
V_DATE VARCHAR2 (10);
BEGIN
SELECT TO_CHAR (SYSDATE, 'hh24:mi:ss') INTO V_DATE FROM DUAL;
IF (V_DATE >= '12:00:01' AND V_DATE < '13:00:00')
THEN
INSERT INTO TABLE ..
ELSE
UPDATE TABLE...
END IF;
END;
I have two tables called sale and customer. I want to create a trigger that updates the column last_purchase on customer table on each new insert in the sale table.
Table customer: customer_id, name, last_sale, ...
Table sale: sale_id, customer_id, date, ...
CREATE TRIGGER update_last_sale BEFORE INSERT ON sale FOR EACH ROW EXECUTE...
I have started writing but I don't know how to do it.
Could someone help me?
CREATE FUNCTION update_customer_last_sale() RETURNS TRIGGER AS $$
BEGIN
UPDATE customer SET last_sale=now() WHERE cutomer_id=NEW.customer_id;
RETURN NEW;
END; $$
LANGUAGE plpgsql;
then
CREATE TRIGGER update_last_sale
BEFORE INSERT ON sale
FOR EACH ROW EXECUTE update_customer_last_sale;
NEW is the row which is about to be inserted in the sale table. (For an update row, it would be NEW for how the row will look after the update, and OLD for how the row looks before the update).
Basically, I don't think it is a good idea to store redundant data. The last_sale column in customers is just an aggregate of max(sales.sale_date).
It even gets worse if we use now() to touch customers.last_date. What would happen if we would need to re-insert some historical records (eg to recompute last year's taxes). That's what you get when you store redundant data....
-- modelled after Erwin's version
SET search_path='tmp';
-- DROP TABLE customers CASCADE;
CREATE TABLE customers
( id INTEGER NOT NULL PRIMARY KEY
, name VARCHAR
, last_sale DATE
);
-- DROP TABLE sales CASCADE;
CREATE TABLE sales
( id INTEGER NOT NULL PRIMARY KEY
, customer_id INTEGER REFERENCES customers(id)
, saledate DATE NOT NULL
);
CREATE OR REPLACE FUNCTION update_customer_last_sale() RETURNS TRIGGER AS $meat$
BEGIN
UPDATE customers cu
-- SET last_sale = now() WHERE id=NEW.customer_id
SET last_sale = (
SELECT MAX(saledate) FROM sales sa
WHERE sa.customer_id=cu.id
)
WHERE cu.id=NEW.customer_id
;
RETURN NEW;
END; $meat$
LANGUAGE plpgsql;
CREATE TRIGGER update_last_sale
AFTER INSERT ON sales
FOR EACH ROW
EXECUTE PROCEDURE update_customer_last_sale();
INSERT INTO customers(id,name,last_sale) VALUES(1, 'Dick', NULL),(2, 'Sue', NULL),(3, 'Bill', NULL);
INSERT INTO sales(id,customer_id,saledate) VALUES (1,1,'1900-01-01'),(2,1,'1950-01-01'),(3,2,'2011-12-15');
SELECT * FROM customers;
SELECT * FROM sales;
The results:
id | name | last_sale
----+------+------------
3 | Bill |
1 | Dick | 1950-01-01
2 | Sue | 2011-12-15
(3 rows)
id | customer_id | saledate
----+-------------+------------
1 | 1 | 1900-01-01
2 | 1 | 1950-01-01
3 | 2 | 2011-12-15
(3 rows)
I think you want the rule here.
CREATE RULE therule AS ON INSERT TO sale DO ALSO
(UPDATE customer SET customer.last_sale = now()
WHERE customer.customer_id=NEW.customer_id);
EDIT: but see the discussion in comments.
I have a table that looks like this
user_id | name | created_on | updated_on
--------------------------------------------------
1 | Peter D | 1/1/2009 |
If I insert or update a record, I'd like a trigger to update the updated_on field with datetime('now'). But I can't find the function name to target the most recently updated row in sqlite3. Is there one?
CREATE TRIGGER your_table_trig AFTER UPDATE ON your_table
BEGIN
update your_table SET updated_on = datetime('now') WHERE user_id = NEW.user_id;
END;
You can specify a trigger to update the updated_on column of your row when the row changes, however, the change invokes the same trigger again and you'll end up with the error too many levels of trigger recursion.
In order to avoid that, you can specify which columns you want the trigger to fire upon and of course you need to exclude updated_on.
CREATE TRIGGER your_table_trig
AFTER UPDATE OF ID,
user_id,
name,
created_on ON your_table
FOR EACH ROW BEGIN UPDATE your_table
SET updated_on = DATETIME ('NOW')
WHERE rowid = new.rowid ; END;