Reg: procedure is giving erro - sql

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)';

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).

procedure is not getting executed

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;
/

Wrong number or types of arguments

Create or replace procedure sp_create_tables as
Lv_str varchar2(1000);
Begin
For I in (select distinct(deptno) from emp) loop
Lv_str:='create table deptno'||I||' select * from emp where
1=2';
Execute immediate lv_str;
End loop;
End;
From what I can understand, your question is probably "why the procedure throws this compilation error"
PLS-00306: wrong number or types of arguments in call to '||'
The reason is that the implicit cursor loop variable I refers to the set of records from the query and not the deptno itself. In order to refer to the deptno, you should use <loop_variable>.deptno. Also 2 other things you should change: The as keyword is missing and DISTINCT is a keyword and you are using it as a function, which works because of default parentheses, but is not the right way to use it.
Create or replace procedure sp_create_tables as
Lv_str varchar2(1000);
Begin
For I in (select distinct dept from emp) loop
Lv_str:='create table deptno'||I.deptno||' as select * from emp where
1=2';
Execute immediate lv_str;
End loop;
End;
In this line of code I is an implicit rowtype variable, with a data structure defined by the projection of the driving select statement:
For I in (select distinct(deptno) from emp) loop
But you are attempting to reference it in your dynamic SQL as though it were an attribute. You need to use a column name instead.
Create or replace procedure sp_create_tables as
Lv_str varchar2(1000);
Begin
For I in (select distinct (deptno) from emp) loop
Lv_str:='create table deptno'|| I.deptno ||
' as select * from emp where 1=2';
Execute immediate lv_str;
End loop;
End;
Incidentally there is a bug in your dynamic SQL statement. The correct syntax is CREATE TABLE ... AS SELECT .... Dynamic SQL is hard because the compiler can't validate the bits of code in strings. Consequently what should be compilation errors manifest themselves as runtime errors. You will find it helpful to instrument your code with some logging (or dbms_output.put_line()) to record the assembled statement before it runs. It makes debugging a lot easier.
" i have got a error saying -01031 insufficient priviliges"
So what this means is your authorisation to create a table was granted through a role. The Oracle security model does not allow us to build PL/SQL programs - or views - using privileges granted through a role. This includes PL/SQL executing DDL through dynamic SQL. You need a DBA user to grant CREATE TABLE to your user directly.

PL/SQL on Oracle 11g code is when executing stored procedure

What I am trying to do here is not run an insert code over and over, which is why I decided to create a stored procedure. Below is the script for the stored procedure and it created successfully, but when I execute the stored procedure "BEGIN SP_INSERT_PMC_UPDATE_DP; END;" I receive an error message "*wrong number or types of arguments in call to SP_INSERT_PMC_UPDATE_DP*"
My frame of thinking and sp code on this is:
1- I check to see if there are records in the PMC_UPDATE_DP and place that value into RECORD_COUNT.
2- Now if RECORD_COUNT is greater than zero I want to clear out all the records from the PMC_UPDATE_DP table.
3- If the RECORD_COUNT is equal to zero, then I want to insert data from the EXTERNAL_PMC_UPDATE_FD table to PMC_UPDATE_DP.
CREATE OR REPLACE PROCEDURE PMC.SP_INSERT_PMC_UPDATE_DP
(RECORD_COUNT OUT NUMBER) --Is my issue here???
IS
BEGIN
--Returns the total records and adds the value to RECORD_COUNT variable.
SELECT COUNT(*)
INTO RECORD_COUNT
FROM PMC.PMC_UPDATE_DP;
--Condition to see if RECORD_COUNT is greater than zero and dumps data to clear out PMC.PMC_UPDATE_DP table.
IF RECORD_COUNT > 0 THEN
EXECUTE IMMEDIATE 'TRUNCATE TABLE PMC.PMC_UPDATE_DP';
END IF;
--Condition to see if RECORD_COUNT equals zero. If true insert data into PMC_UPDATE_DP table from the PMC.EXTERNAL_PMC_UPDATE_FD table.
IF RECORD_COUNT = 0 THEN
INSERT INTO PMC.PMC_UPDATE_DP
( JOB_ID,
CONTROL_ID,
ACCT_NO,
CALC_DIVIDEND,
CERT_NEW_SHARES,
CALC_CASH_DISBURSMENT,
DECEASED,
STATUS,
ALPHA_SP1,
ALPHA_SP2,
ALPHA_SP3,
ALPHA_SP4,
NUM_SP1,
NUM_SP2,
NUM_SP3,
NUM_SP4,
DONT_CALL,
DONT_MAIL
)
SELECT JOB_ID,
CONTROL_ID,
ACCT_NO,
CALC_DIVIDEND,
CERT_NEW_SHARES,
CALC_CASH_DISBURSMENT,
DECEASED,
STATUS,
ALPHA_SP1,
ALPHA_SP2,
ALPHA_SP3,
ALPHA_SP4,
NUM_SP1,
NUM_SP2,
NUM_SP3,
NUM_SP4,
DONT_CALL,
DONT_MAIL
FROM PMC.EXTERNAL_PMC_UPDATE_FD
COMMIT;
END IF;
END;
If you want to call a procedure with an OUT parameter, you'd need to pass in a local variable so Oracle has something to hold the output.
DECLARE
l_record_count pls_integer;
BEGIN
PMC.SP_INSERT_PMC_UPDATE_DP ( l_record_count );
-- Do something with l_record_count, probably not just calling dbms_output
-- dbms_output.put_line( 'Record count = ' || l_record_count );
END;
If you are not planning on doing anything with the ouput, perhaps you really don't want the procedure to have an OUT parameter. Perhaps you just want to declare record_count as a local variable.

Can not have CREATE TABLE inside if else

When I run this query
DECLARE
num NUMBER;
BEGIN
SELECT COUNT(*) INTO num FROM user_all_tables WHERE TABLE_NAME=upper('DatabaseScriptLog')
;
IF num < 1 THEN
CREATE TABLE DatabaseScriptLog
(ScriptIdentifier VARCHAR(100) NOT NULL,
ScriptType VARCHAR(50),
StartDate TIMESTAMP,
EndDate TIMESTAMP,
PRIMARY KEY (ScriptIdentifier)
);
END IF;
END;
When execute the above, I got the following:
PLS-00103: Encountered the symbol
"CREATE" when expecting one of the
following:
begin case declare exit for goto if
loop mod null pragma raise return
select update while with << close current delete
fetch lock insert open rollback
savepoint set sql execute commit
forall merge pipe
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
You cannot run DDL statements like that. You need to use dynamic SQL (EXECUTE IMMEDIATE).
IF num < 1 THEN
EXECUTE IMMEDIATE 'CREATE TABLE DatabaseScriptLog (ScriptIdentifier VARCHAR(100) NOT NULL, ScriptType VARCHAR(50), StartDate TIMESTAMP, EndDate TIMESTAMP, PRIMARY KEY (ScriptIdentifier))'
END IF;
You cannot do this like you could in SQLServer. You need to execute the create code through a stored procedure that is already in the proper schema. You pass the create code as a parameter and the stored procedure that has the correct privileges does it for you.
I use a version script that updates the schema to the latest by running schema altering operations separated by if-then clauses to check what version the db is at. After altering it increments the version so that the next if statements test passes and so on. If you are up to date and run the script the ifs skip all altering code. If your db is at version 46 and you run the script which has all changes up to 50, you execute only the blocks that represent versions 47-50.
You could execute immediate but would need elevated privileges which I would not recommend.
Hope this helps.