Update trigger assistance - sql

Evening, needing assistance regarding triggers.
Within my development environment I have two tables, one containing employee data (which contains various data errors that will be amended via ALTER TABLE) and the log table.
How do i go about designing a trigger that will update multiple rows contained within the log table such as 'issue_status','status_update_date' when ALTER TABLE sql is used to amend the data contained in the first data table?
-- employee table
CREATE TABLE emp(
emp_id INTEGER NOT NULL,
emp_name VARCHAR(30),
emp_postcode VARCHAR(20),
emp_registered DATE,
CONSTRAINT pk_emp PRIMARY KEY (emp_id));
-- SQL for the log table
CREATE TABLE data_log
(issue_id NUMBER(2) NOT NULL,
table_name VARCHAR2(20),
row_name VARCHAR2(20),
data_error_code NUMBER(2),
issue_desc VARCHAR2(50),
issue_date DATE,
issue_status VARCHAR2(20),
status_update_date DATE);
-- example log insert
INSERT INTO data_log( SELECT DI_SEQ.nextval, 'emp', emp.emp_id, '1', 'emp_name case size not UPPER', SYSDATE, 'un-fixed', '' FROM emp WHERE emp_name != UPPER(emp_name));
This is the example of the issue inserted into the log table. All i want to do is if I update the emp table to set 'emp_name' to Upper the trigger will register this update and change the rows 'issue_status' and 'status_update_date' to 'fixed' and the 'sysdate' of when the change was made
I've done some browsing however i'm still struggling to understand, any literature recommendations would be appreciated also.
Thanks in advance for the assistance.

Note that we don't use ALTER TABLE to update the rows of a table as you have mentioned.We use Update statement. Your trigger should be a BEFORE UPDATE TRIGGER like this.
CREATE OR REPLACE TRIGGER trig_emp_upd BEFORE
UPDATE ON emp
FOR EACH ROW
WHEN ( new.emp_name = upper(old.emp_name) )
BEGIN
UPDATE data_log
SET
issue_status = 'fixed',
status_update_date = SYSDATE
WHERE
row_name =:new.emp_id;
END;
/

Related

how to insert data using trigger

I got question about using trigger to insert data, for instance, I do have two tables, and second table has attributes and records with table, except additional two attributes, like below:
CREATE TABLE dept
(
DEPTNO NUMBER(3) PRIMARY KEY,
DNAME VARCHAR2(16),
LOC VARCHAR2(16)
);
CREATE TABLE dept_shadow
(
DEPTNO NUMBER(3) PRIMARY KEY,
DNAME VARCHAR2(16),
LOC VARCHAR2(16),
USER VARCHAR2(32),
MODTIME CHAR(17)
);
and I want create a trigger to track all inserts into a table.
surprisedly, I got error about creating table:
Error starting at line : 11 in command -
CREATE TABLE dept_shadow
(
DEPTNO NUMBER(3) PRIMARY KEY,
DNAME VARCHAR2(16),
LOC VARCHAR2(16),
USER VARCHAR2(32),
MODTIME CHAR(17)
)
Error report -
ORA-00904: : invalid identifier
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
I have no idea about this error and does anyone can tell me how to do this job by create trigger? Since there is no actual records to insert! Any suggestions are appreciated
Okay, so the error you are getting is because oracle (like all databases) has some reserved words. Now I'm not 100% sure because I don't tend to work with Oracle DB all that often, but I would assume that you cannot use the word USER for that reason. Try using USERNAME or USERDESCRIPTION or something like that instead.
Now for the trigger:
CREATE OR REPLACE TRIGGER trg_shadow
BEFORE INSERT OR UPDATE OR DELETE
ON dept_shadow
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
MODTIME char (17);
BEGIN
IF INSERTING
THEN
-- do something
ELSIF UPDATING
THEN
-- do something
ELSIF DELETING
THEN
-- do something
END IF;
From there, you can access "new" data through :NEW and "old" data through :OLD.
EDIT:
The difference of a BEFORE and AFTER trigger is when they are executed and both have a valid use.
BEFORE triggers may be used to validate data BEFORE inserting or updating. So for instance if you don't want to update the rows that would otherwise have the value 0 in column x.
AFTER triggers may be used to validate the new data AFTER inserting. So for instance if you want to delete all rows that now have the value 0 in column x.
It doesn't really matter in your case though.
Hope that helps!
The error is because of the column name(user) you are using in the dept_shadow table.
USER is predefined that's why it won't work as column name in table. Rename it to 'audit_user' or anything you want, which is not keyword. It will work seamlessly.
And for trigger
CREATE OR REPLACE TRIGGER dept_trigger
AFTER INSERT
ON dept
FOR EACH ROW
DECLARE
v_username varchar2(10);
BEGIN
-- Find username of person performing the INSERT into the table
SELECT user INTO v_username
FROM dual;
-- Insert record into shadow table
INSERT INTO dept_shadow
( DEPTNO,
DNAME,
LOC,
AUDIT_USER,
MODTIME )
VALUES
( :new.DEPTNO,
:new.DNAME,
:new.LOC,
v_username,
:new.MODTIME
);
END;
/
Hope this will work for you.

Restoring a Truncated Table from a Backup

I am restoring the data of a truncated table in an Oracle Database from an exported csv file. However, I find that the primary key auto-increments and does not insert the actual values of the primary key constrained column from the backed up file.
I intend to do the following:
1. drop the primary key
2. import the table data
3. add primary key constraints on the required column
Is this a good approach? If not, what is recommended? Thanks.
EDIT: After more investigation, I observed there's a trigger to generate nextval on a sequence to be inserted into the primary key column. This is the source of the predicament. Hence, following the procedure above would not solve the problem. It lies in the trigger (and/or sequence) on the table. This is solved!
easier to use your .csv as an external table and then go
create table your_table_temp as select * from external table
examine the data in the new temp table to ensure you know what range of primary keys is present
do a merge into the new table
samples from here and here
CREATE TABLE countries_ext (
country_code VARCHAR2(5),
country_name VARCHAR2(50),
country_language VARCHAR2(50)
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY ext_tab_data
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ','
MISSING FIELD VALUES ARE NULL
(
country_code CHAR(5),
country_name CHAR(50),
country_language CHAR(50)
)
)
LOCATION ('Countries1.txt','Countries2.txt')
)
PARALLEL 5
REJECT LIMIT UNLIMITED;
and the merge
MERGE INTO employees e
USING hr_records h
ON (e.id = h.emp_id)
WHEN MATCHED THEN
UPDATE SET e.address = h.address
WHEN NOT MATCHED THEN
INSERT (id, address)
VALUES (h.emp_id, h.address);
Edit: after you have merged the data you can drop the temp table and the result is your previous table with the old data and the new data together
Edit you mention " During imports, the primary key column does not insert from the file, but auto-increments". This can only happen when there is a trigger on the table, likely, Before insert on each row. Disable the trigger and then do your import. Re-enable the trigger after committing your inserts.
I used the following procedure to solve it:
drop trigger trigger_name
Imported the table data into target table
drop sequence sequence_name
CREATE SEQUENCE SEQ_NAME INCREMENT BY 1 START WITH start_index_for_next_val MAXVALUE max_val MINVALUE 1 NOCYCLECACHE 20 NOORDER
CREATE OR REPLACE TRIGGER "schema_name"."trigger_name"
before insert on target_table
for each row
begin
select seq_name.nextval
into :new.unique_column_name
from dual;
end;

sql error 04098 invalid trigger

I need some assistance with troubleshooting the trigger that I'm trying to create/use for logging updates and inserts on a table.
I'm using a customers_history table to track all the changes being made on the customers table.
CREATE TABLE customers (
custID INTEGER PRIMARY KEY,
custFName VARCHAR2(30),
custLName VARCHAR2(30),
custState CHAR(20),
custZip NUMBER(5)
);
-- log inserts and updates on customers table
CREATE TABLE customers_history (
histID INTEGER PRIMARY KEY,
cID INTEGER,
cFName VARCHAR2(30),
cLName VARCHAR2(30),
cState CHAR(20),
cZip NUMBER(5)
);
Also, for the histID I'm using a sequence to auto increment the histID on customers_history table.
CREATE SEQUENCE ch_seq
MINVALUE 1
START WITH 1
INCREMENT BY 1;
CREATE OR REPLACE TRIGGER audit_customers
BEFORE UPDATE
OR INSERT ON customers
FOR EACH ROW
BEGIN
INSERT INTO customers_history(histID,cID,cFName,cLName,cState,cZip)
VALUES(ch_seq.nextval,:NEW.custID,:NEW.custFName,:NEW.custLName,
:NEW.custState,:NEW.custZip);
END;
/
I have been inserting two rows on customers prior to creating the trigger, and they work fine. After I create the trigger, it will not allow me to insert anymore rows on customers and it also throws the ORA-04098: trigger 'SYSTEM.AUDIT_CUSTOMERS' is invalid and failed re-validation 04098. 00000 - "trigger '%s.%s' is invalid and failed re-validation" error message.
I've tried to see if there is any code errors using select * from user_errors where type = 'TRIGGER' and name = 'audit_customers'; and it returned no lines. Not sure if that helps or not. Thanks.
you have created your trigger for multiple DML operations (Insert and Update) so you need to specipy the DML operation by using IF INSERTING THEN....END IF; and IF UPDATING THEN....END IF; for example:
CREATE OR REPLACE TRIGGER audit_customers
BEFORE UPDATE
OR INSERT ON customers
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO customers_history(histID,cID,cFName,cLName,cState,cZip)
VALUES(ch_seq.nextval,:NEW.custID,:NEW.custFName,:NEW.custLName,
:NEW.custState,:NEW.custZip);
END IF;
END;
/
I went through and changed a couple things and it seems to work for both insert and update operations. I dropped the tables, sequence, and trigger, then did the tried the following code and it works now. Thank you all for your time and inputs!
CREATE TABLE customers (
custID INTEGER,
custFName VARCHAR2(30),
custLName VARCHAR2(30),
custState CHAR(20),
custZip NUMBER(5)
);
CREATE TABLE customers_history (
histID INTEGER,
cID INTEGER,
cFName VARCHAR2(30),
cLName VARCHAR2(30),
cState CHAR(20),
cZip NUMBER(5)
);
CREATE OR REPLACE TRIGGER audit_customers
BEFORE UPDATE
OR INSERT ON customers
FOR EACH ROW
BEGIN
INSERT INTO customers_history(
histID,
cID,
cFName,
cLName,
cState,
cZip
)
VALUES(
ch_seq.nextval,
:new.custID,
:new.custFName,
:new.custLName,
:new.custState,
:new.custZip
);
END;
/
I used the same sequence code as before, added a couple of rows, and it worked. Then I altered the two tables, by adding the primary keys,
ALTER TABLE customers ADD PRIMARY KEY(custID);
ALTER TABLE customers_history ADD PRIMARY KEY(histID);
inserted a couple other columns, modified a few rows, and it still worked. I'm a happy camper, although I'm not certain what or how it actually got fixed.

Oracle - Insert Stored Procedure Foreign Key

Instructions:
Create two tables, named employees and departments. Preface the table names with your initials. Link the two tables (foreign key) by a column called dept. Make up a few column names for each table.
Employees Table:
create table bsemployees(
dept number primary key,
empName varchar2(20),
salary number
);
Departments Table:
create table bsdepartments(
dept number references bsemployees(dept),
deptName varchar2(20)
);
Write the following stored procedures:
• Insert a row into the employees table. If the department does not exist. Insert it into the departments table.
create or replace procedure sp_employees(
a_dept IN number,
a_empName IN varchar2,
a_salary IN number
)
as
vCount number;
BEGIN
sp_check_dept(a_dept,vCount);
insert into bsemployees values(a_dept, a_empName, a_salary);
if vCount = 0 then
dbms_output.put_line('**DEPT DOES NOT EXIST**');
insert into bsdepartments (dept, deptName) values(a_dept, NULL);
end if;
END;
/
create or replace procedure sp_check_dept(
a_dept IN number,
vCount OUT number
)
as
BEGIN
select count(*)
into vCount
from bsdepartments
where dept = a_dept;
end;
/
• Insert a row into the departments table.
create or replace procedure sp_departments(
a_dept IN number,
a_deptName IN varchar2
)
as
BEGIN
insert into bsdepartments values(a_dept, a_deptName);
END;
/
I've got it pretty much all down for this assignment except for the fact that when I try to insert a row into the departments table I am getting a integrity constraint - parent key not found error.
If I do execute sp_employees(5, 'John Doe', 90000); It will display ***DEPT DOES NOT EXIST*** and will go ahead and insert the data into bsemployees and insert the dept# into bsdepartments and the deptName will be left blank based on my if-then statement. Doing a select(*) shows me this.
However if I go ahead and do execute sp_departments(1, 'human resources'); to place a row into departments I get the parent key error. I understand that I am trying to insert something that has no parent key but I do not know how to fix it.
Your table design isn't quite correct - the dept primary key needs to be added as a foreign key to employee (not as the primary key), and employee should have its own primary key:
create table bsdepartments(
dept number primary key,
deptName varchar2(20)
);
create table bsemployees(
empName varchar2(20) primary key,
dept number references bsdepartments(dept),
salary number
);
You can then do the 'add if not exists' logic in the check_dept proc:
create or replace procedure sp_check_dept(
a_dept IN number
)
as
vCount number
BEGIN
select count(*)
into vCount
from bsdepartments
where dept = a_dept;
if (vCount = 0) then
dbms_output.put_line('**DEPT DOES NOT EXIST**');
insert into bsdepartments (dept, deptName) values(a_dept, NULL);
end if;
end;
Which then simplifies the employee insertion proc as it should be guaranteed of a department:
create or replace procedure sp_insertEmployee(
a_dept IN number,
a_empName IN varchar2,
a_salary IN number
)
as
BEGIN
sp_check_dept(a_dept);
insert into bsemployees values(a_dept, a_empName, a_salary);
END
Notes
Recommend that you name the procs in alignment with their purpose, e.g. insertEmployee vs just employees
As you've noted, the problem with the 'add if not exists' approach is that you do not have sufficient data to completely populate the department table, hence the null column (but this is what your lecturer asked for)
Your realation to table department is bad. It should linked as below
create table bsdepartments(
dept number primary key,
deptName varchar2(20)
);
and it should be linked to employee table
create table bsemployees(
dept number references bsdepartments(dept),
empName varchar2(20),
salary number
);
Then if you try inserting execute sp_departments(1, 'human resources'); it will execute
an then you have to insert the employee.
Here the employee is related to department not the department is related to employee.

How to use PostgreSql trigger and procedure to audit/restore parent-child tables

We want to keep the editing history of some table and restore them if necessary. For example, we have following tables. We want to audit the emp table when insert /delete action performed. Besides these, when update on version field happens, we also need to save a copy of related emp_addr records to emp_addr_audit. When users want to roll back to a specif version of one emp, we need to restore the record from emp_audit and emp_addr_audit.
I am thinking to use a trigger to do audit work and a procedure to do restore work. I know the key part is how to maintain the integrity of parent-child tables in audit and restore work. I need some advices. Thanks.
create table emp (
emp_id integer primary key,
version varchar(50)
);
/* Address table */
create table emp_addr (
addr_id integer primary key,
emp_id integer, -- references table emp
line1 varchar(30),
);
/* Audit table for emp table */
create table emp_audit (
operation character(1),
updatetime timestamp,
emp_id integer,
version varchar(50)
);
/* Audit table for emp_addr table */
create table emp_addr_audit (
operation character(1),
addr_id integer,
emp_id integer,
line1 varchar(30),
);
I'd recommend adding id field as a primary key to audit tables, since you need to reference to the emp table when rolling back to it and you also need that emp to reference corresponding version of emp_addr.
So audit table DDLs should look like this:
/* Audit table for emp table */
create table emp_audit (
id bigserial,
operation character(1),
updatetime timestamp,
emp_id integer,
version varchar(50),
CONSTRAINT audit_emp_id PRIMARY KEY (id)
);
/* Audit table for emp_addr table */
create table emp_addr_audit (
id bigserial,
operation character(1),
addr_id integer,
emp_id integer,
line1 varchar(30),
CONSTRAINT fk_ audit_emp_id FOREIGN KEY emp_audit_id
REFERENCES emp_audit (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE;
);
Next you will need to create a trigger, to store changes. Note, that you will have to monitor changes in both tables and create references to the corresponding stored records.
CREATE TRIGGER t_audit_emp_IUD
AFTER INSERT OR UPDATE OR DELETE -- probably u want only update. Not sure
ON emp
FOR EACH ROW
EXECUTE PROCEDURE emp_modified();
CREATE TRIGGER t_audit_emp_addr_IUD
AFTER INSERT OR UPDATE OR DELETE
ON emp_addr
FOR EACH ROW
EXECUTE PROCEDURE emp_addr_modified();
And finally define the functions. Note, that functions should be stored in database before triggers, since triggers reference to the functions.
Rollback function should take emp_audit.id as an input and restore the state according to the audit table. It would be a good idea to save state before rolling back to prevent possible data loss.
If this doesn't answer your question, please clarify which part do you actually need help with.