procedure is not getting executed - sql

trying to run below procedure, but temp table is not getting updated with TEMP_JOB_ID_FROM_JOB_DOC_' and date.
create or replace PROCEDURE scheduler_cleanup
(P_IN_DATE IN DATE)
IS
BEGIN
EXECUTE IMMEDIATE 'create table TEMP_ID_STAT_TIME_FRM_JOB_DOC as select JOB_ID, last_update_time_utc, status from J_DOC where
LAST_UPDATE_TIME_UTC <= TRUNC(SYSDATE) - 90 and status=''Sent''';
EXECUTE IMMEDIATE 'DELETE hub_sign
WHERE job_id IN ( SELECT JOB_ID
FROM TEMP_ID_STAT_TIME_FRM_JOB_DOC )';
EXECUTE IMMEDIATE 'DELETE j_doc
WHERE job_id IN ( SELECT JOB_ID
FROM TEMP_ID_STAT_TIME_FRM_JOB_DOC )';
--EXECUTE IMMEDIATE 'RENAME TABLE TEMP_ID_STAT_TIME_FRM_JOB_DOC TO TEMP_JOB_ID_FROM_JOB_DOC_1119';
--BEGIN
-- EXECUTE IMMEDIATE 'DROP TABLE TEMP_JOB_ID_FROM_JOB_DOC_1119';
--EXCEPTION
--WHEN OTHER THEN
-- NULL;
-- END;
EXECUTE IMMEDIATE 'ALTER TABLE TEMP_ID_STAT_TIME_FRM_JOB_DOC RENAME TO || TO_CHAR(P_IN_DATE, 'MMYY');
END;

That's the last ALTER; you're renaming a table to TO_CHAR(30-11-2020, 'MMYY') which - when executed - does this:
SQL> alter table temp rename to to_char(sysdate, 'mmy');
alter table temp rename to to_char(sysdate, 'mmy')
*
ERROR at line 1:
ORA-14047: ALTER TABLE|INDEX RENAME may not be combined with other operations
Should have been just RENAME, not ALTER:
SQL> begin
2 execute immediate 'rename temp to temp_' || to_char(sysdate, 'mmy');
3 end;
4 /
PL/SQL procedure successfully completed.
SQL> select * From tab where tname like 'TEMP%';
TNAME TABTYPE CLUSTERID
------------------------------ ------- ----------
TEMP_120 TABLE
SQL>
Though, do you really really want to do it this way? In Oracle, we usually don't create tables dynamically. Renaming them so that they contain date component scales as a goat which flies (i.e. doesn't scale at all). How will you write queries against bunch of tables whose name differs by the "date" suffix? Dynamic SQL again? Well, good luck with that.
Two other options I can think of:
an "ordinary" table with a date column; filter rows by including that column into the WHERE clause (don't forget to index the column)
partitioning

As suggested by Littlefoot also, You can achieve the desired behavior with minimal dynamic queries.
If you really want to store the intermediate data then you can use the GLOBAL TEMPORARY TABLE or PL/SQL collections.
I am giving an example of how you can use the global temporary table as follows:
Create the Global temporary table:
-- use the data type of the column as per requirement
CREATE GLOBAL TEMPORARY TABLE GT_TABLE (
JOB_ID NUMBER,
LAST_UPDATE_TIME_UTC TIMESTAMP,
STATUS VARCHAR2(100)
) ON COMMIT DELETE ROWS;
Use the global temporary table in the procedure:
CREATE OR REPLACE PROCEDURE SCHEDULER_CLEANUP (
P_IN_DATE IN DATE
) IS
BEGIN
INSERT INTO GT_TABLE
SELECT JOB_ID,
LAST_UPDATE_TIME_UTC,
STATUS
FROM J_DOC
WHERE LAST_UPDATE_TIME_UTC <= TRUNC(SYSDATE) - 90
AND STATUS = 'Sent';
DELETE FROM HUB_SIGN
WHERE JOB_ID IN (
SELECT JOB_ID
FROM GT_TABLE
);
DELETE FROM J_DOC
WHERE JOB_ID IN (
SELECT JOB_ID
FROM GT_TABLE
);
EXECUTE IMMEDIATE 'create table TEMP_JOB_ID_FROM_JOB_DOC_' || TO_CHAR(P_IN_DATE,'MMYY')
|| ' as select JOB_ID, last_update_time_utc, status from GT_TABLE ';
END;
/

Related

PL/SQL: Creation and usage of a temporary table in script

On an Oracle 19c DB I need to sample on customers whilst keeping a uniorm distribution to model the moving of customers.
There are (on purpose) multiple rows with the same customers but changeing adderssses to model moving. Thus, for the sampling i need to group first.
Now what I got is that for the SAMPLE clause the source has to be materialized. So in the PL/SQL script I generate a temporary table that i want to use afterwards with SAMPLE.
But even a simple SELECT INTO afterwards doens't work.
set SERVEROUT on;
DECLARE
v_cust_name VARCHAR2(100);
cmd_creation VARCHAR2(500):='CREATE PRIVATE TEMPORARY TABLE ORA$PTT_temp_cust AS(
SELECT cust_id, MIN(name) as name
FROM customers
GROUP BY cust_id)';
BEGIN
EXECUTE IMMEDIATE cmd_creation;
dbms_output.put_line('temp created');
SELECT name
INTO v_cust_name
FROM (SELECT *
FROM ORA$PTT_temp_cust SAMPLE(5))
WHERE ROWNUM =1;
EXECUTE IMMEDIATE 'DROP TABLE ORA$PTT_temp_cust';
dbms_output.put_line('temp dropped');
END;
What I get is this
ORA-06550: line 15, column 18:
PL/SQL: ORA-00942: table or view does not exist
The table gets created. That far I got when I only executed the String for the creation and nothing else. Then I can access the table in a desired script from a different point.
Does this have to do with the compiling? Is there some different way to solve this?
Your PL/SQL Block simple does not compile as the table you query does not exists at the time of compilation.
You must perform event the query with execute immediate
Simplified Example
DECLARE
v_cust_name VARCHAR2(100);
cmd_creation VARCHAR2(500):='CREATE PRIVATE TEMPORARY TABLE ORA$PTT_temp_cust AS select * from dual';
BEGIN
EXECUTE IMMEDIATE cmd_creation;
dbms_output.put_line('temp created');
EXECUTE IMMEDIATE 'SELECT DUMMY FROM ORA$PTT_temp_cust' INTO v_cust_name;
dbms_output.put_line('name' || v_cust_name);
EXECUTE IMMEDIATE 'DROP TABLE ORA$PTT_temp_cust';
dbms_output.put_line('temp dropped');
END;
/
An other caveat the can lead to ORA-00942: table or view does not existin your setup is that you performs commit in your script.
The default definition of the ON COMMIT clause is DROP DEFINITION so you must use
CREATE PRIVATE TEMPORARY TABLE ORA$PTT_temp_cust
ON COMMIT PRESERVE DEFINITION
...
Dynamic SQL is evil. The fact that you created the table using dynamic SQL (your 1st execute immediate) doesn't mean that Oracle "predicts" you'll actually succeed with that and "presume" that statements that follow are correct. Nope - that table doesn't exist yet, so everything has to be moved to dynamic SQL.
Something like this (disregard changes in table and column names I used and global vs. private temporary table; this is 11gXE):
SQL> DECLARE
2 v_cust_name VARCHAR2 (100);
3 cmd_creation VARCHAR2 (500)
4 := 'CREATE global TEMPORARY TABLE PTT_temp_cust AS
5 SELECT empno, MIN(ename) as name
6 FROM emp
7 GROUP BY empno';
8 BEGIN
9 EXECUTE IMMEDIATE cmd_creation;
10
11 EXECUTE IMMEDIATE '
12 SELECT max(name)
13 FROM (SELECT *
14 FROM PTT_temp_cust SAMPLE(5))
15 WHERE ROWNUM = 1'
16 INTO v_cust_name;
17
18 EXECUTE IMMEDIATE 'DROP TABLE PTT_temp_cust';
19
20 DBMS_OUTPUT.put_line ('Result = ' || v_cust_name);
21 END;
22 /
Result =
PL/SQL procedure successfully completed.
SQL>
I got no result, though - but you should (at least, I hope so).

I can query data from partitions, but not drop the partitions (ORA-14006)?

I have a table called schema.exampletab, which has partitions where the partition name is on the form Pxxxxxx where the x's are partition periods (year and month YYYYMM) and then there is a local PK. I want to drop a partition, but I get the error message ORA-14006: Invalid partition name when attempting to drop it, but when I try to simply query data from a partition it selects and displays the data from the partition in question. Note that I am using a scheduler which uses date as a parameter using YYYYMMDD, so I usually substring.
For instance I can query data by using the following statement:
SELECT *
FROM schema.exampletab
PARTITION (P202110);
This returns 20 rows of data in my table.
I then try to drop the partition using the following statement:
ALTER TABLE schema.exampletab DROP PARTITION (concat(P,substr('20211011',1,6))
UPDATE INDEXES;
This however, leads to the ORA-14006 error message. In order to control that the partition exists I tried to look up that the schema, table, and partition exists in the all_tab_partitions where all partitions in the database I work on are logged.
select partition_name from all_tab_partitions
where table_owner = 'schema' and table_name = 'exampletab' and
substr(partition_name,2,7) = substr('20211022',1,6);
This returns the partition name P202110 in the query result.
I hoped I could use the drop partition with a subquery like this:
ALTER TABLE schema.exampletab DROP PARTITION select partition_name from all_tab_partitions
where table_owner = 'schema' and table_name = 'exampletab' and
substr(partition_name,2,7) = substr('20211022',1,6);
However, this still leads to ORA-14006 error. I have tried to write the partition name like 'P202110' and P202110 instead of the parenteces, but no luck.
How can I write the drop partition statement such that it drops the partition instead of giving the ORA-14006 error?
This is something which I have to do routinely and so it would be nice to know how to properly select and drop partitions, or truncate them, etc. Also, I use a scheduler which runs the sql queries at specified intervals so there I have to specify dates as a parameter, meaning that in my example code the YYYYMM is a parametervalue that gets parsed in and so I need to concactinate the P with this outputted parameter value, because if I type only P202110 it actually drops it.
You have two options, keeping in consideration that you are using sysdate to know which partition you should drop.
Option 1 -> Dynamic SQL and the result must be executed outside the query
select ' alter table '||table_owner||'.'||table_name||' drop partition '||partition_name||' update indexes ; '
from
dba_Tab_partitions
where table_owner = 'your_schema'
and table_name = 'your_table'
and partition_name = 'P'||substr(to_char(sysdate,'yyyymmdd'),1,6) ; ;
This query gives you the command output, but you need to execute it
alter table yourschema.yourtable drop partition P202110 update indexes;
Example in my own environment
SQL> select ' alter table '||table_owner||'.'||table_name||' drop partition '||partition_name||' update indexes ; '
from
dba_Tab_partitions
where table_owner = 'FDM_DATA'
and table_name = 'FDM_DIM_CUSTOMER'
and partition_name = 'P_'||substr(to_char(sysdate,'yyyymmdd'),1,6) ;
'ALTERTABLE'||TABLE_OWNER||'.'||TABLE_NAME||'DROPPARTITION'||PARTITION_NAME||'UP
--------------------------------------------------------------------------------
alter table FDM_DATA.FDM_DIM_CUSTOMER drop partition P_202110 update indexes ;
Option 2 -> PLSQL
A better option is to use PLSQL. A small example when you want to drop only one partition based on the current sysdate. You can extend / modify this code to cover any kind of time frame.
declare
v_owner varchar2(128) := 'YOUR_SCHEMA';
v_table_name varchar2(128) := 'YOUR_TABLE';
v_partition_name varchar2(128);
begin
select partition_name into v_partition_name from all_tab_partitions
where table_owner = v_owner and
table_name = v_table_name and
partition_name = 'P'||substr(to_char(sysdate,'yyyymmdd'),1,6) ;
execute immediate 'alter table '||v_owner||'.'||v_table_name||' drop partition '||v_partition_name||' update indexes' ;
exception
when no_data_found then null; -- if there is no partition, nothing to do and no error is raised
when others then raise;
end;
/
You cannot use "expressions" in partition name
This is wrong:
ALTER TABLE schema.exampletab DROP PARTITION (concat(P,substr('20211011',1,6))
UPDATE INDEXES;
this is correct:
ALTER TABLE schema.exampletab DROP PARTITION P20211011
UPDATE INDEXES;
You may use this code to drop partitions dynamically. Just update C_OWNER ,C_TABLE_NAME , C_PARTITION_TEMPLATE in the header. Please pay attention that the sql command is built dynamically, but when it is ready is has the partition name fully parsed.
declare
C_OWNER varchar2(128) := 'MYOWNER';
C_TABLE_NAME varchar2(128) := 'MYTABLE';
C_PARTITION_TEMPLATE varchar2(128) := '2011';
cursor part_cur is
select *
from all_tab_partitions
where table_owner=C_OWNER and
table_name = C_TABLE_NAME and
partition_name like '%'||C_PARTITION_TEMPLATE||'%'
order by partition_position;
BEGIN
for part_rec in part_cur loop
execute immediate 'ALTER TABLE "'||part_rec.table_owner||'"."'||part_rec.table_name||'"'||
' DROP PARTITION ("'||part_rec.partition_name||'") UPDATE INDEXES';
end loop;
END;
/

Reg: procedure is giving erro

Below is my procedure giving error below, kindly check and confirm.
create or replace PROCEDURE CLEANUP_rec
IS
BEGIN
EXECUTE IMMEDIATE 'create table TEMP_JOB_ID_FROM_JOB_DOC_1119 as select JOB_ID, last_update_time_utc, status from J_DOC where
LAST_UPDATE_TIME_UTC <= TRUNC(SYSDATE) - 90 and status=''Sent''';
delete from HUB_SIG where JOB_id IN ( SELECT JOB_ID
FROM TEMP_JOB_ID_FROM_JOB_DOC_1119);
delete from J_DOC
where JOB_id IN ( SELECT JOB_ID
FROM TEMP_JOB_ID_FROM_JOB_DOC_1119);
EXECUTE IMMEDIATE 'RENAME TABLE TEMP_JOB_ID_FROM_JOB_DOC_1119 TO TEMP_ID_STAT_TIME_FRM_JOB_DOC';
END;
Giving below error
Project: C:\Users\bc8807\AppData\Roaming\SQL Developer\system4.0.1.14.48\o.sqldeveloper.12.2.0.14.48\projects\IdeConnections#database_zltv9883.jpr
t1c3d231_db_connection
Error(10,1): PL/SQL: SQL Statement ignored
Error(11,8): PL/SQL: ORA-00942: table or view does not exist
Error(13,3): PL/SQL: SQL Statement ignored
Error(15,8): PL/SQL: ORA-00942: table or view does not exist
Kindly suggest how can we resolve this issue.
so kindly confirm is below is correct:
create or replace PROCEDURE CLEANUP_AUTOMATION
IS
BEGIN
EXECUTE IMMEDIATE 'create table TEMP_JOB_ID_FROM_JOB_DOC_1119 as select JOB_ID, last_update_time_utc, status from J_DOC where
LAST_UPDATE_TIME_UTC <= TRUNC(SYSDATE) - 90 and status=''Sent''';
execute immediate 'delete from HUB_SIG where JOB_id IN ( SELECT JOB_ID
FROM TEMP_JOB_ID_FROM_JOB_DOC_1119)';
execute immediate 'delete from J_DOC
where JOB_id IN ( SELECT JOB_ID
FROM TEMP_JOB_ID_FROM_JOB_DOC_1119)';
EXECUTE IMMEDIATE 'RENAME TABLE TEMP_JOB_ID_FROM_JOB_DOC_1119 TO TEMP_ID_STAT_TIME_FRM_JOB_DOC';
END;
Also Next step would be to automate them inside procedure. we must get the date as input and pass that
when rename the table (RENAME TEMP_JOB_ID_FROM_JOB_DOC_1119 TO TEMP_ID_STAT_TIME_FRM_JOB_DOC)
and schedule this procedure in DBMS_SCHEDULER to run every night at 10 PM PST.
Kindly suggest
The issue here is that you are trying to compile a procedure, thus checking that all the code is correct, which uses a table that, at compile time, does not exist.
No matter what you have in the execute immediate string, you can't write a statement that uses something that does not exist.
If you strictly need to create this table at runtine, use it and then rename it, you need to use dynamic SQL for all the statements that use your table:
execute immediate 'delete from HUB_SIG where JOB_id IN ( SELECT JOB_ID
FROM TEMP_JOB_ID_FROM_JOB_DOC_1119)';
execute immediate 'delete from J_DOC
where JOB_id IN ( SELECT JOB_ID
FROM TEMP_JOB_ID_FROM_JOB_DOC_1119)';

Oracle Dynamically create script for Updating Partitioned table

I have a range partitioned Oracle table which I want to update using partition key.
Is there a way to dynamically create update script which takes into account partition key as follows, WITHOUT having to manually maintain such script
update table where date between 'a' and 'b'
update table where date between 'b' and 'c'.
I think it would not be necessary to specify the range in the where clause to update individual partitions. You may use the partition_extension_clause of update dynamically.
BEGIN
FOR r IN (
SELECT partition_name
FROM user_tab_partitions
WHERE table_name = 'YOUR_TABLE'
) LOOP
EXECUTE IMMEDIATE 'UPDATE YOUR_TABLE (' || r.partition_name || ') SET somecol = somevalue where someother_clause';
COMMIT; --if it's necessary
END LOOP;
END;
/

Change table name with sysdate

I want to change a table name by appending SYSDATE to it. For example, I want to change table EXAMPLE_TABLE to EXAMPLE_TABLE_05_01_2015, but I want to get the date from SYSDATE.
I prepared the following but it is not working:
ALTER TABLE "MYDB"."EXAMPLE_TABLE" rename to (SELECT 'EXAMPLE_TABLE' || TO_CHAR(SYSDATE, '_dd_MM_yyyy') FROM DUAL);
How can I make it work?
Here is the error:
SQL Error: ORA-14047: ALTER TABLE|INDEX RENAME may not be combined with other operations
14047. 00000 - "ALTER TABLE|INDEX RENAME may not be combined with other operations"
*Cause: ALTER TABLE or ALTER INDEX statement attempted to combine
a RENAME operation with some other operation which is illegal
*Action: Ensure that RENAME operation is the sole operation specified in
ALTER TABLE or ALTER INDEX statement;
Use execute immediate.
begin
execute immediate
'alter table mydb.example_table rename to ' ||
'example_table_' || to_char(sysdate, 'dd_mm_yyyy');
end;
/
That said, I have the hunch that you'd be better off using partitioned tables.
In SQL*Plus, you could use the variable substitution.
Just another way :
SQL> CREATE TABLE t(ID NUMBER)
2 /
Table created.
SQL>
SQL> COLUMN new_tablename NEW_VALUE new_tablename
SQL> SELECT 't_' || to_char(sysdate, 'dd_mm_yyyy') AS new_tablename from dual
2 /
NEW_TABLENAM
------------
t_05_01_2015
SQL>
SQL> RENAME t TO &new_tablename
2 /
old 1: RENAME t TO &new_tablename
new 1: RENAME t TO t_05_01_2015
Table renamed.
SQL>
SQL> select * from t_05_01_2015;
no rows selected
SQL>
So, now the table T is renamed to T_05_01_2015.