I wrote a procedure to dump dba_audit_trail information to a flat file and once it done purge the sys.audit table. Execute the procedure manually it dump the data correctly to flat file. But it execute as a schedule job TIMESTAMP is not correct. TIMESTAMP is 6 hours less than TIMESTAMP in dba_audit_trail table.
Table content
------------------------
31-07-2014 07:26:08,EOMCBOFD,CFNET\CBOEDPS5,Administrator,CBOEDPS5,LOGON,,
31-07-2014 07:26:24,EOMCBOFD,CFNET\CBOEDPS5,Administrator,CBOEDPS5,LOGOFF BY CLEANUP,,
Flat file content
-------------------
31-07-2014 01:26:08,EOMCBOFD,CFNET\CBOEDPS5,Administrator,CBOEDPS5,LOGON,,
31-07-2014 01:26:24,EOMCBOFD,CFNET\CBOEDPS5,Administrator,CBOEDPS5,LOGOFF BY CLEANUP,,
CREATE OR REPLACE PROCEDURE SYS.ddl_audit_log_purge AS
CURSOR cur_date IS`enter code here`
SELECT sysdate FROM dual;
rec1 cur_date%ROWTYPE;
created_file_name VARCHAR2(100);
file_name UTL_FILE.FILE_TYPE;
BEGIN
OPEN cur_date;
LOOP
FETCH cur_date INTO rec1;
EXIT WHEN cur_date%NOTFOUND;
created_file_name:=rec1.sysdate;
file_name:=UTL_FILE.FOPEN('AUD_DIR','cfcbo_'||created_file_name||'.exp','W');
FOR rec in(
SELECT to_char(TIMESTAMP, 'dd-mm-yyyy HH24:MI:SS')
||','||username
||','||userhost
||','||os_username
||','||terminal
||','||action_name
||','||owner
||','||obj_name
out_line
FROM dba_audit_trail)
LOOP
UTL_FILE.PUTF(file_name,'%s\n',rec.out_line);
UTL_FILE.FFLUSH(file_name);
END LOOP;
UTL_FILE.FCLOSE(file_name);
END LOOP;
CLOSE cur_date;
insert into aud_log_job (now,task) values(sysdate,'exported audit table');
execute immediate 'truncate table sys.aud$';
insert into aud_log_job(now,task) values(sysdate,'truncate audit table');
END;
/
This is actually quite complicated issue. Check this Oracle support node:
DBMS_SCHEDULER or DBMS_JOB And DST / Timezones Explained (Doc ID 467722.1)
"The sysdate and systimestamp "seen" by jobs may be different then the one when you connect, the used timezone is the one that was set when the database (!) was started."
Related
Create or replace procedure PROC AS
V_TABLE_NAME VARCHAR2(255);
V_LIST SYS_REFCURSOR;
DATE_VALUE_INS VARCHAR2(10);
BEGIN
DATE_VALUE_INS:=TO_CHAR(SYSDATE,'YYMMDD');
OPEN V_LIST FOR
SELECT NAME FROM DW.table_name_list ;
LOOP
FETCH V_LIST
INTO V_TABLE_NAME;
EXIT WHEN V_LIST%NOTFOUND;
EXECUTE IMMEDIATE 'CREATE TABLE Schema.'||V_TABLE_NAME||'_'||DATE_VALUE_INS||' AS SELECT * FROM DW.'||V_TABLE_NAME;
END LOOP;
CLOSE V_LIST;
end;
I have created this Proc which takes value from a table which has Table_name and create Backup using Execute Immediate.
Now the requirement has changed that i only need to create backup for partial records (i.e. where clause on each table )
I have 6 tables as such .
New Approach i am thinking is :
EXECUTE IMMEDIATE 'CREATE TABLE Schema.'||V_TABLE_NAME||'_'||DATE_VALUE_INS||' AS SELECT * FROM DW.'||V_TABLE_NAME where some condition;
But the problem becomes all 6 have different column to filter on.
My Ask is How should I change my design of proc to Adjust this new Requirement.
6 tables? Why bother? Create a procedure which - depending on table name passed as a parameter - in IF-THEN-ELSE runs 6 different CREATE TABLE statements.
On the other hand, another approach would be to create backup tables in advance (at SQL level), add BACKUP_DATE column to each of them, and - in procedure - just perform INSERT operation which doesn't require dynamic SQL at all.
For example:
create table emp_backup as select * from emp where 1 = 2;
alter table emp_backup add backup_date date;
create or replace procedure p_backup (par_table_name in varchar2) is
begin
if par_table_name = 'EMP' then
insert into emp_backup (empno, ename, job, sal, backup_date)
select empno, ename, job, sal, trunc(sysdate)
from emp
where deptno = 20; --> here's your WHERE condition
elsif par_table_name = 'DEPT' then
insert into dept_backup (...)
select ..., trunc(sysdate)
from dept
where loc = 'DALLAS';
elsif ...
...
end if;
end;
/
Doing so, you'd easier access backup data as you'd query only one table, filtered by BACKUP_DATE. That's also good if you have to search for some data that changed several days ago, but you don't know exact day. What would you rather do: query 10 tables (and still not find what you're looking for), or query just one table and find that info immediately?
I am new to PL/SQL and Oracle APEX. In oracle APEX, I'm trying to create a trigger that will fire when employees are not present after a certain time, say for example 12:00 PM and it's not friday.
I have these columns in the Attendances Table: emp_id, work_date, attend_stat.
The emp_id is a foreign key referenced from Employees Table
When it's past 12:00 PM, and there was no attendance entry for a particular employee, I want a row inserted for that employee like this:
insert into attendances(emp_id, work_date, attend_Stat)
values("ID OF NOT PRESENT EMPLOYEE", sysdate, 'A');
I have written this pl/sql statement but I cannot find a way to implement it or figure out will it even work.
declare
cursor c_emp_id is
select emp_id from employees;
cursor c_emp_at is
select emp_id from attendances where work_date = sysdate and to_char(sysdate,'DAY') <> 'FRIDAY';
begin
for i in c_emp_id loop
for a in c_emp_at loop
if i.emp_id <> a.emp_id then
insert into attendances(emp_id, work_date, attend_stat)
values(i.emp_id, systimestamp, 'A');
end if;
end loop;
end loop;
exception
when no_data_found then
for i in c_emp_id loop
insert into attendances(emp_id, work_date, attend_stat)
values(i.emp_id, systimestamp, 'A');
end loop;
end;
How can I achieve this?
You need to save the code in a package or in a stored procedure and set it to run as a database job. See the documentation for details.
Please note the doc is for version 11 of the database, check the version you are on
select version from v$instance
and google for actual docs
I have written below pl sql block and trying to create a procedue. But i am getting warnings and not able to execute the procedue.
Please suggest if something i am missing \
Please let me know if this question is duplicate as i am not able to get the exact link to refer
create or replace PROCEDURE EmployeeProc
IS
BEGIN
delete from Employeetable where EmplId in (
select EmployeeId FROM EmployeeMstrTbl where JoiningDate between to_date('2019-01-01','YYYY-MM-DD') and to_date('2019-02-28','YYYY-MM-DD'));
commit;
DBMS_OUTPUT.PUT_LINE('Deleted '||SQL%ROWCOUNT ||' records from Employeetable');
END;
Error: Object Invalid
Try using cursor
CREATE OR REPLACE PROCEDURE EMPLOYEEPROC IS
CURSOR C1 IS
SELECT EMPLOYEEID
FROM EMPLOYEEMSTRTBL
WHERE JOININGDATE BETWEEN TO_DATE('2019-01-01','YYYY-MM-DD') AND TO_DATE('2019-02-28','YYYY-MM-DD'));
BEGIN
FOR I IN C1 LOOP
DELETE FROM EMPLOYEETABLE
WHERE EMPLID=I.EMPLOYEEID;
END LOOP;
COMMIT;
DBMS_OUTPUT.PUT_LINE('DELETED '||SQL%ROWCOUNT ||' RECORDS FROM EMPLOYEETABLE');
END;
Your code works just fine.
CREATE TABLE Employeetable
(
EmplId NUMBER
);
CREATE TABLE EmployeeMstrTbl
(
EmployeeId NUMBER,
JoiningDate DATE
);
CREATE OR REPLACE PROCEDURE EmployeeProc
IS
BEGIN
DELETE FROM Employeetable
WHERE EmplId IN
(SELECT EmployeeId
FROM EmployeeMstrTbl
WHERE JoiningDate BETWEEN TO_DATE ('2019-01-01',
'YYYY-MM-DD')
AND TO_DATE ('2019-02-28',
'YYYY-MM-DD'));
COMMIT;
DBMS_OUTPUT.PUT_LINE (
'Deleted ' || SQL%ROWCOUNT || ' records from Employeetable');
END;
EXEC EmployeeProc;
DROP TABLE Employeetable;
DROP TABLE EmployeeMstrTbl;
DROP PROCEDURE EmployeeProc;
Script output:
Table created.
Table created.
Procedure created.
PL/SQL procedure successfully completed.
Table dropped.
Table dropped.
Procedure dropped.
DBMS Output:
Deleted 0 records from Employeetable
Maybe you have a typo in a table name, column name or something similar.
I suggest that you try to execute your delete statement first to check if it works.
Not sure if perhaps you mistyped something, or if it is to do with how you have it set up. But even if it is a mistype and it could work, it's not nice, so do a loop.
i = 0;
FOR r in (select * FROM EmployeeMstrTbl where JoiningDate between to_date('2019-01-01','YYYY-MM-DD') and to_date('2019-02-28','YYYY-MM-DD'))
LOOP
DELETE FROM Employeetable where EmplId = r.EmployeeId;
END LOOP;
DBMS_OUTPUT.PUT_LINE('Deleted '|| i ||' records from Employeetable');
Because this will work, and more importantly, its easier to understand. Keeping code short and abbreviated has become much less important nowadays since the size of the code is almost never the problem, but keeping it easy to understand is extremely important so that it can be maintained in the future.
I am performing an archival process on a huge database and it involves deleting the production active table and renaming another table to be the new production table. When dropping the production active table, the triggers also get deleted. So I am just taking a backup of the triggers defined on my table using
select * from all_triggers where table_name=mytablename;
My question is, can I directly copy these triggers in to the all_triggers table after I rename my other table to be the new production active table? Will the triggers still work?
Same question for defining indexes and constraints too.
Copying the triggers from one table to another can be done by copying DDL, and not updating all_triggers table. This can be done by using DBMS_METADATA.
The closest practical example I found here: Copy Triggers when you Copy a Table
The following script can be amended as per your need:
declare
p_src_tbl varchar2(30):= 'PERSONS'; --your table name
p_trg_tbl varchar2(30):= 'PSN2'; --your trigger name
l_ddl varchar2(32000);
begin
execute immediate 'create table '||p_trg_tbl||' as select * from '||p_src_tbl||' where 1=2';
for trg in (select trigger_name from user_triggers where table_name = p_src_tbl) loop
l_ddl:= cast(replace(replace(dbms_metadata.get_ddl( 'TRIGGER', trg.trigger_name),p_src_tbl,p_trg_tbl),trg.trigger_name,substr(p_trg_tbl||trg.trigger_name, 1, 30)) as varchar2);
execute immediate substr(l_ddl, 1, instr(l_ddl,'ALTER TRIGGER')-1);
end loop;
end;
/
No, you cannot directly manipulate data dictionary tables. You can't insert data directly into all_triggers (the same goes for any data dictionary table). I guess you probably could given enough hacking. It just wouldn't work and would render your database unsupported.
The correct way to go is to script out your triggers and reapply them later. If you want to do this programmatically, you can use the dbms_metadata package. If you want to get the DDL for each of the triggers on a table, you can do something like
select dbms_metadata.get_ddl( 'TRIGGER', t.trigger_name, t.owner )
from all_triggers t
where table_owner = <<owner of table>>
and table_name = <<name of table>>
To replicate your scenario i have prepared below snippet. Let me know if this helps.
--Simple example to copy Trigger from one table to another
CREATE TABLE EMP_V1 AS
SELECT * FROM EMP;
--Creating Trigger on Old Table for Example purpose
CREATE OR REPLACE TRIGGER EMP_OLD_TRIGGER
AFTER INSERT OR UPDATE ON EMP FOR EACH ROW
DECLARE
LV_ERR_CODE_OUT NUMBER;
LV_ERR_MSG_OUT VARCHAR2(2000);
BEGIN
dbms_output.put_line('Your code for data Manipulations');
--Like Insert update or DELETE activities
END;
-- To replace this trigger for emp_v2 table
set serveroutput on;
DECLARE
lv_var LONG;
BEGIN
FOR i IN (
SELECT OWNER,TRIGGER_NAME,DBMS_METADATA.GET_DDL('TRIGGER','EMP_OLD_TRIGGER') ddl_script FROM all_triggers
WHERE OWNER = 'AVROY') LOOP
NULL;
lv_var:=REPLACE(i.ddl_script,'ON EMP FOR EACH ROW','ON EMP_V1 FOR EACH ROW');
dbms_output.put_line(substr(lv_var,1,INSTR(lv_var,'ALTER TRIGGER',1)-1));
EXECUTE IMMEDIATE 'DROP TRIGGER '||I.TRIGGER_NAME;
EXECUTE IMMEDIATE lv_var;
END LOOP;
END;
--Check if DDL manipulation has been done for not
SELECT OWNER,TRIGGER_NAME,DBMS_METADATA.GET_DDL('TRIGGER','EMP_OLD_TRIGGER') ddl_script FROM all_triggers
WHERE OWNER = 'AVROY';
---------------------------------OUTPUT----------------------------------------
"
CREATE OR REPLACE TRIGGER "AVROY"."EMP_OLD_TRIGGER"
AFTER INSERT OR UPDATE ON EMP_V1 FOR EACH ROW
DECLARE
LV_ERR_CODE_OUT NUMBER;
LV_ERR_MSG_OUT VARCHAR2(2000);
BEGIN
dbms_output.put_line('Your code for data Manipulations');
--Like Insert update or DELETE activities
END;
"
-----------------------------OUTPUT----------------------------------------------
I have a trigger which is sending data from a table to another table in another database. all s working fine.
The prob is that there is a new concept of END DATE, in which, if END DATE is present for a person, the row should reach the other table on that particular date..
eg.: if someones end date is 31st august, it should go on that day only, but ofcourse, my trigger is firing on event change (when enddate is set to 31st august)..
Could you please suggest me what I can do to SET the row to go on ENDDATE.?
It doesn't sound like you don't want a trigger. It sounds like you want a job. For example, if you create a procedure that transfers all the rows whose end_date is today
CREATE OR REPLACE PROCEDURE move_rows_with_end_date(
p_end_date IN DATE DEFAULT trunc(SYSDATE)
)
AS
BEGIN
INSERT INTO table_name#remote_database( <<list of columns>> )
SELECT <<list of columns>>
FROM table_name
WHERE end_date = trunc(sysdate);
END;
Then you can create a job that runs the procedure every day at midnight
DECLARE
l_jobno PLS_INTEGER;
BEGIN
dbms_job.submit( l_jobno,
'BEGIN move_rows_with_end_date; END;',
trunc(sysdate+1),
'trunc(sysdate+1)' );
commit;
END;