checking the validity of a stored procedure statement - sql

I am trying to execute this stored procedure statement,
CREATE OR REPLACE PROCEDURE table_records_select IS
TYPE loc_array_type IS TABLE OF VARCHAR2(100)
INDEX BY binary_integer;
dml_str VARCHAR2 (200);
loc_array loc_array_type;
BEGIN
-- bulk fetch the list of tables
SELECT table_name BULK COLLECT INTO loc_array
FROM all_tab_columns;
-- for each table, delete the records where EXCN_ID matches with EXCN_ID of t_int_excn_log table where excn_strt_tm < sysdate-7
FOR i IN loc_array.first..loc_array.last LOOP
dml_str := 'select B.* from t_int_excn_log A,'
|| loc_array(i) || ' B'
||'where A.excn_strt_tm < sysdate-7 and A.excn_id=B.excn_id';
EXECUTE IMMEDIATE dml_str ;
END LOOP;
END;
/
SHOW ERRORS;
It seems that stored procedure is created successfully and showing valid.
But when I try to execute it, It shows various errors,
ORA-00933: SQL command not properly ended
ORA-06512: at "HIAB_UAT.TABLE_RECORDS_SELECT", line 19
ORA-06512: at line 3
In the end it says 'Procedure Completed'
Can anyone help me on this.

There is one mistake in your Dynamic SQL Preparation: at second line of "dml_str" preparation. Space missed just after alias "B".
Your Code:
|| loc_array(i) || ' B'
||'where A.excn_strt_tm < sysdate-7 and A.excn_id=B.excn_id';
It should be:
|| loc_array(i) || ' B '
||'where A.excn_strt_tm < sysdate-7 and A.excn_id=B.excn_id';
CREATE OR REPLACE PROCEDURE table_records_select IS
TYPE loc_array_type IS TABLE OF VARCHAR2(100)
INDEX BY binary_integer;
dml_str VARCHAR2 (200);
loc_array loc_array_type;
BEGIN
-- bulk fetch the list of tables
SELECT table_name BULK COLLECT INTO loc_array
FROM user_tab_columns;--all_tab_columns;
-- for each table, delete the records where EXCN_ID matches with EXCN_ID of t_int_excn_log table where excn_strt_tm < sysdate-7
FOR i IN loc_array.first..loc_array.last LOOP
dml_str := 'select B.* from t_int_excn_log A,'
|| loc_array(i) || ' B '
||'where A.excn_strt_tm < sysdate-7 and A.excn_id=B.excn_id';
EXECUTE IMMEDIATE dml_str ;
END LOOP;
END;

Related

PL/SQL Dynamic SQL : Table name not valid

I'm currently learning PL/SQL. I need to create a PL/SQL block to create a backup of all my tables like this : myTable -> myTable_old.
Here's what I got right now :
DECLARE
Cursor c IS SELECT table_name
FROM user_tables
WHERE table_name NOT LIKE '%_old';
sql_slc VARCHAR2(200);
sql_drp VARCHAR2(200);
sql_crt VARCHAR2(200);
row_count NUMBER;
t_name user_tables.table_name%type;
t_backup_name user_tables.table_name%type;
BEGIN
sql_drp := 'DROP TABLE :1 CASCADE';
sql_crt := 'CREATE TABLE :1 AS SELECT * FROM :2';
sql_slc := 'SELECT COUNT(*) FROM user_tables WHERE table_name = :1';
OPEN c;
LOOP
FETCH c INTO t_name;
EXIT WHEN (c%NOTFOUND);
t_backup_name := t_name || '_old';
dbms_output.put_line(t_name || ' ' || t_backup_name);
EXECUTE IMMEDIATE sql_slc INTO row_count USING t_backup_name;
IF row_count > 0 THEN
dbms_output.put_line(t_backup_name || ' dropped');
EXECUTE IMMEDIATE sql_drp USING t_backup_name;
END IF;
dbms_output.put_line(t_backup_name || ' created');
EXECUTE IMMEDIATE sql_crt USING t_backup_name, t_name;
COMMIT;
END LOOP;
CLOSE c;
END;
/
Here's the error :
OUVRAGE OUVRAGE_old
OUVRAGE_old created
DECLARE
*
ERROR on line 1 :
ORA-00903: table name not valid
ORA-06512: on line 29
I don't understand why this error is coming up, can someone help me ?
The issue is that you can not use bind variables for table names; Oracle documentation:
The database uses the values of bind variables exclusively and does
not interpret their contents in any way.
You should edit your code to use concatenation instead:
DECLARE
Cursor c IS SELECT table_name
FROM user_tables
WHERE table_name NOT LIKE '%_OLD'; /* OLD, upper case */
sql_slc VARCHAR2(200);
--sql_drp VARCHAR2(200);
--sql_crt VARCHAR2(200);
row_count NUMBER;
t_name user_tables.table_name%type;
t_backup_name user_tables.table_name%type;
BEGIN
-- sql_drp := 'DROP TABLE :1 CASCADE';
-- sql_crt := 'CREATE TABLE :1 AS SELECT * FROM :2';
sql_slc := 'SELECT COUNT(*) FROM user_tables WHERE table_name = :1';
OPEN c;
LOOP
FETCH c INTO t_name;
EXIT WHEN (c%NOTFOUND);
t_backup_name := t_name || '_OLD'; /* OLD, upper case */
DBMS_OUTPUT.put_line (t_name || ' ' || t_backup_name);
EXECUTE IMMEDIATE sql_slc INTO row_count USING t_backup_name;
IF row_count > 0
THEN
DBMS_OUTPUT.put_line (t_backup_name || ' dropped');
-- EXECUTE IMMEDIATE sql_drp USING t_backup_name;
EXECUTE IMMEDIATE ' drop table ' || t_backup_name; /* concatenation and not bind variables */
END IF;
DBMS_OUTPUT.put_line (t_backup_name || ' created'); /* concatenation and not bind variables */
-- EXECUTE IMMEDIATE sql_crt USING t_backup_name, t_name;
EXECUTE IMMEDIATE 'create table ' || t_backup_name || ' as select * from ' || t_name;
COMMIT;
END LOOP;
CLOSE c;
END;
Also, notice that, if not double quoted, object names always are uppercase, so you have to look for t_name || '_OLD' and not t_name || '_old'

Oracle PL/SQL How to store and fetch a dynamic multi column query

I am trying hard dynamic PL/SQL thing here.
I don't manage to fetch a column dynamic Query.
I am iterating on the name of the column to concatenate a full query in order to be executed on another table.
sql_req := 'select ';
for c in (SELECT name_col from TAB_LISTCOL)
loop
sql_req := sql_req || 'sum(' || c.name_col || '),';
end loop;
sql_req := sql_req || ' from ANOTHER_TAB ';
And when i try to execute it with EXECUTE IMMEDIATE or cursors or INTO/BULK COLLECT thing or just to fetch, i don't manage to iterate on the result.
I tried a lot.
Can you help me plz ? Or maybe it is not possible ?
ps : i know the coma is wrong but my code is more complexe than this : i didn't want to put more things
If you only want to get string columns, you can use listagg
select listagg(name_col, ',') WITHIN GROUP (ORDER BY null) from TAB_LISTCOL
Please see if this helps
In the absence of actual table structure and requirement, I'm creating dummy tables and query to illustrate an example:
SQL> create table another_tab
as
select 10 dummy_value1, 100 dummy_value2, 1000 dummy_value3 from dual union all
select 11 dummy_value1, 101 dummy_value2, 1001 dummy_value3 from dual union all
select 12 dummy_value1, 102 dummy_value2, 1003 dummy_value3 from dual
;
Table created.
SQL> create table tab_listcol
as select column_name from dba_tab_cols where table_name = 'ANOTHER_TAB'
;
Table created.
To reduce complexity in the final block, I'm defining a function to generate the dynamic sql query. This is based on your example and will need changes according to your actual requirement.
SQL> create or replace function gen_col_based_query
return varchar2
as
l_query varchar2(4000);
begin
l_query := 'select ';
for cols in ( select column_name cname from tab_listcol )
loop
l_query := l_query || 'sum(' || cols.cname || '), ' ;
end loop;
l_query := rtrim(l_query,', ') || ' from another_tab';
return l_query;
end;
/
Function created.
Sample output from the function will be as follows
SQL> select gen_col_based_query as query from dual;
QUERY
--------------------------------------------------------------------------------
select sum(DUMMY_VALUE1), sum(DUMMY_VALUE2), sum(DUMMY_VALUE3) from another_tab
Below is a sample block for executing a dynamic cursor using DBMS_SQL. For your ease of understanding, I've added comments wherever possible. More info here.
SQL> set serveroutput on size unlimited
SQL> declare
sql_stmt clob;
src_cur sys_refcursor;
curid number;
desctab dbms_sql.desc_tab; -- collection type
colcnt number;
namevar varchar2 (50);
numvar number;
datevar date;
l_header varchar2 (4000);
l_out_rows varchar2 (4000);
begin
/* Generate dynamic sql from the function defined earlier */
select gen_col_based_query into sql_stmt from dual;
/* Open cursor variable for this dynamic sql */
open src_cur for sql_stmt;
/* To fetch the data, however, you cannot use the cursor variable, since the number of elements fetched is unknown at complile time.
Therefore you use DBMS_SQL.TO_CURSOR_NUMBER to convert a REF CURSOR variable to a SQL cursor number which you can then pass to DBMS_SQL subprograms
*/
curid := dbms_sql.to_cursor_number (src_cur);
/* Use DBMS_SQL.DESCRIBE_COLUMNS to describe columns of your dynamic cursor, returning information about each column in an associative array of records viz., desctab. The no. of columns is returned in colcnt variable.
*/
dbms_sql.describe_columns (curid, colcnt, desctab);
/* Define columns at runtime based on the data type (number, date or varchar2 - you may add to the list)
*/
for indx in 1 .. colcnt
loop
if desctab (indx).col_type = 2 -- number data type
then
dbms_sql.define_column (curid, indx, numvar);
elsif desctab (indx).col_type = 12 -- date data type
then
dbms_sql.define_column (curid, indx, datevar);
else -- assuming string
dbms_sql.define_column (curid, indx, namevar, 100);
end if;
end loop;
/* Print header row */
for i in 1 .. desctab.count loop
l_header := l_header || ' | ' || rpad(desctab(i).col_name,20);
end loop;
l_header := l_header || ' | ' ;
dbms_output.put_line(l_header);
/* Loop to retrieve each row of data identified by the dynamic cursor and print output rows
*/
while dbms_sql.fetch_rows (curid) > 0
loop
for indx in 1 .. colcnt
loop
if (desctab (indx).col_type = 2) -- number data type
then
dbms_sql.column_value (curid, indx, numvar);
l_out_rows := l_out_rows || ' | ' || rpad(numvar,20);
elsif (desctab (indx).col_type = 12) -- date data type
then
dbms_sql.column_value (curid, indx, datevar);
l_out_rows := l_out_rows || ' | ' || rpad(datevar,20);
elsif (desctab (indx).col_type = 1) -- varchar2 data type
then
dbms_sql.column_value (curid, indx, namevar);
l_out_rows := l_out_rows || ' | ' || rpad(namevar,20);
end if;
end loop;
l_out_rows := l_out_rows || ' | ' ;
dbms_output.put_line(l_out_rows);
end loop;
dbms_sql.close_cursor (curid);
end;
/
PL/SQL procedure successfully completed.
Output
| SUM(DUMMY_VALUE1) | SUM(DUMMY_VALUE2) | SUM(DUMMY_VALUE3) |
| 33 | 303 | 3004 |
You have to use EXECUTE IMMEDIATE with BULK COLLECT
Below is an example of the same. For more information refer this link
DECLARE
TYPE name_salary_rt IS RECORD (
name VARCHAR2 (1000),
salary NUMBER
);
TYPE name_salary_aat IS TABLE OF name_salary_rt
INDEX BY PLS_INTEGER;
l_employees name_salary_aat;
BEGIN
EXECUTE IMMEDIATE
q'[select first_name || ' ' || last_name, salary
from hr.employees
order by salary desc]'
BULK COLLECT INTO l_employees;
FOR indx IN 1 .. l_employees.COUNT
LOOP
DBMS_OUTPUT.put_line (l_employees (indx).name);
END LOOP;
END;
If I understand correctly, you want to create a query and execute it and return the result to another function or some calling app. As the resulting query's columns are note before-known, I'd return a ref cursor in this case:
create function get_sums return sys_refcur as
declare
my_cursor sys_refcursor;
v_query varchar2(32757);
begin
select
'select ' ||
listagg('sum(' || name_col || ')', ', ') within group (order by name_col) ||
' from another_tab'
into v_query
from tab_listcol;
open my_cursor for v_query;
return v_query;
end get_sums;

configuring the oracle procedure so that it should accept the input value

I have created an procedure which delete the 3 months old partition from the table now i want to make the below procedure configurable enough such that it should accept the count from the user so please advise how can i modify the below procedure
create or replace
PROCEDURE Delete_partitions
/*
This procedure will delete partitions for the following tables:
TEMPTABLE
*/
BEGIN
FOR cc IN
(
SELECT partition_name, high_value
FROM user_tab_partitions
WHERE table_name = 'TEMPTABLE'
)
dbms_output.put_line('starting to drop partition ');
LOOP
EXECUTE IMMEDIATE 'BEGIN
IF sysdate >= ADD_MONTHS(' || cc.high_value || ', 2) THEN
EXECUTE IMMEDIATE
''ALTER TABLE TEMPTABLE DROP PARTITION ' || cc.partition_name || '
'';
END IF;
dbms_output.put_line('drop partition completed');
END;';
END LOOP;
exception
when others then
dbms_output.put_line(SQLERRM);
END;
/
Add some parameters to it:
create or replace
PROCEDURE Delete_partitions(cnt in number)
/*
This procedure will delete partitions for the following tables:
TEMPTABLE
*/
BEGIN
FOR cc IN
(
SELECT partition_name, high_value
FROM user_tab_partitions
WHERE table_name = 'TEMPTABLE'
)
dbms_output.put_line('starting to drop partition ');
LOOP
EXECUTE IMMEDIATE 'BEGIN
IF sysdate >= ADD_MONTHS(' || cc.high_value || ', ' || cnt-1 || ') THEN
EXECUTE IMMEDIATE
''ALTER TABLE TEMPTABLE DROP PARTITION ' || cc.partition_name || '
'';
END IF;
dbms_output.put_line('drop partition completed');
END;';
END LOOP;
exception
when others then
dbms_output.put_line(SQLERRM);
END;
/

Dynamic Oracle Procedure - issue with structure

I seem to be having some issues around creating a stored procedure and I simply cannot see where the issue lies. I am relatively new to Oracle.
I have a table of unknown length. What I have done is I created a stored procedure that will create a table to the width of the row count of the table where I am getting my values from. i.e. if the table has 10 values, the code will create a new table 10 columns wide, etc.
I have managed to get the initial code working, but now I am trying to add some additional logic without success.
My logic that I am trying to accomplish is as such:
IF TABLE DOES NOT EXIST - CREATE IT
IF TABLE EXSITS - SIMPLY INSERT INTO IT
I have not created the code for the INSERT part yet as I cannot get the first part to work. Everything was working fine until I added the count and IF statement.
CREATE OR REPLACE PROCEDURE "MDWPROD"."WORKFLOW_VAR_PIVOT" IS
v_sql varchar2(32767);
-- for the first run ofthe procedure, we need to create the table
DECLARE var_count INT;
SELECT
COUNT(*)
INTO
var_count
FROM
all_tables
WHERE
OWNER = 'MDWPROD'
AND TABLE_NAME = 'RBI_PROCESSVARIABLE_WK';
-- if var_count = 0 then the table does not exists, create it, otherwise proceed with other logic
IF var_count = 0 THEN
-- cursor to find out the maximum number of projected columns required
CURSOR cur_proj_test IS
SELECT DISTINCT
ID,
VARIABLE_REPORT_LBL
FROM
MDWPROD.RBI_VARIABLETYPE_DM
ORDER BY
ID;
-- We now loop through the cursor, and build of the SQL string to CREATE and POPULATE the table
BEGIN
v_sql := 'CREATE TABLE MDWPROD.RBI_PROCESSVARIABLE_WK AS SELECT VAR.PROCESS_ID';
FOR i IN cur_proj_test
LOOP
-- dynamically add to the projection for the query
v_sql := v_sql || ',MAX(CASE VT.VARIABLE_REPORT_LBL WHEN ''' || i.VARIABLE_REPORT_LBL || ''' THEN VAR.VALUE ELSE '''' END) AS "' || i.VARIABLE_REPORT_LBL || '"';
END LOOP;
v_sql := v_sql || ' FROM MDWPROD.RBI_VARIABLE_DM VAR INNER JOIN MDWPROD.RBI_VARIABLETYPE_DM VT ON VAR.VARIABLE_TYPE_ID = VT.ID WHERE VAR.CURRENT_IND = ''Y'' GROUP BY VAR.PROCESS_ID order by VAR.PROCESS_ID';
-- Create table and populate it with all the relevant variable values
EXECUTE IMMEDIATE v_sql;
END;
END IF;
END;
Any assistance would be greatly appreciated.
Original working proc:
CREATE OR REPLACE PROCEDURE WORKFLOW_VAR_PIVOT IS
v_sql varchar2(32767);
-- cursor to find out the maximum number of projected columns required
CURSOR cur_proj_test IS
SELECT DISTINCT
ID,
VARIABLE_REPORT_LBL
FROM
MDWPROD.RBI_VARIABLETYPE_DM
ORDER BY
ID;
-- We now loop through the cursor, and build of the SQL string to CREATE and POPULATE the table
BEGIN
v_sql := 'CREATE TABLE MDWPROD.RBI_PROCESSVARIABLE AS SELECT VAR.PROCESS_ID';
FOR i IN cur_proj_test
LOOP
-- dynamically add to the projection for the query
v_sql := v_sql || ',MAX(CASE VT.VARIABLE_REPORT_LBL WHEN ''' || i.VARIABLE_REPORT_LBL || ''' THEN VAR.VALUE ELSE '''' END) AS "' || i.VARIABLE_REPORT_LBL || '"';
END LOOP;
v_sql := v_sql || ' FROM MDWPROD.RBI_VARIABLE_DM VAR INNER JOIN MDWPROD.RBI_VARIABLETYPE_DM VT ON VAR.VARIABLE_TYPE_ID = VT.ID WHERE VAR.CURRENT_IND = ''Y'' GROUP BY VAR.PROCESS_ID order by VAR.PROCESS_ID';
-- un comment this line to print out the entire SQL statement
-- dbms_output.put_line('Dynamic SQL Statement:-' || chr(10) || v_sql || chr(10) || chr(10));
-- DROP TABLE before recreating it
EXECUTE IMMEDIATE 'DROP TABLE MDWPROD.RBI_PROCESSVARIABLE';
-- Create table and populate it with all the relevant variable values
EXECUTE IMMEDIATE v_sql;
END;
There may be other problems (you never stated your exact error), but I can immediately see that the begin keyword is in the wrong place. Your declarations should be at the top, before the begin keyword, and your execution statements should come after.
I moved things around a little. This should get you closer to your goal:
CREATE OR REPLACE PROCEDURE "MDWPROD"."WORKFLOW_VAR_PIVOT" IS
v_sql varchar2(32767);
-- for the first run ofthe procedure, we need to create the table
DECLARE var_count INT;
-- cursor to find out the maximum number of projected columns required
CURSOR cur_proj_test IS
SELECT DISTINCT
ID,
VARIABLE_REPORT_LBL
FROM
MDWPROD.RBI_VARIABLETYPE_DM
ORDER BY
ID;
BEGIN
SELECT
COUNT(*)
INTO
var_count
FROM
all_tables
WHERE
OWNER = 'MDWPROD'
AND TABLE_NAME = 'RBI_PROCESSVARIABLE_WK';
-- if var_count = 0 then the table does not exists, create it, otherwise proceed with other logic
IF var_count = 0 THEN
-- We now loop through the cursor, and build of the SQL string to CREATE and POPULATE the table
v_sql := 'CREATE TABLE MDWPROD.RBI_PROCESSVARIABLE_WK AS SELECT VAR.PROCESS_ID';
FOR i IN cur_proj_test
LOOP
-- dynamically add to the projection for the query
v_sql := v_sql || ',MAX(CASE VT.VARIABLE_REPORT_LBL WHEN ''' || i.VARIABLE_REPORT_LBL || ''' THEN VAR.VALUE ELSE '''' END) AS "' || i.VARIABLE_REPORT_LBL || '"';
END LOOP;
v_sql := v_sql || ' FROM MDWPROD.RBI_VARIABLE_DM VAR INNER JOIN MDWPROD.RBI_VARIABLETYPE_DM VT ON VAR.VARIABLE_TYPE_ID = VT.ID WHERE VAR.CURRENT_IND = ''Y'' GROUP BY VAR.PROCESS_ID order by VAR.PROCESS_ID';
-- Create table and populate it with all the relevant variable values
EXECUTE IMMEDIATE v_sql;
END IF;
END;

Oracle procedure cursor loop errors

Can you tell me what is wrong with this procedure that I'm trying to create?
CREATE OR REPLACE PROCEDURE create_audit_tables (table_owner VARCHAR2)
IS
CURSOR c_tables (
table_owner VARCHAR2)
IS
SELECT ot.owner AS owner, ot.table_name AS table_name
FROM all_tables ot
WHERE ot.owner = table_owner
AND ot.table_name NOT LIKE 'AUDIT_%'
AND ot.table_name <> 'EXAUDIT'
AND NOT EXISTS
(SELECT 1
FROM EXAUDIT efa
WHERE ot.table_name = efa.tname)
AND NOT EXISTS
(SELECT 1
FROM all_tables at
WHERE at.table_name = 'AUDIT_'||ot.table_name);
v_sql VARCHAR2 (8000);
v_count NUMBER := 0;
v_aud VARCHAR2 (30);
BEGIN
FOR r_table IN c_tables (table_owner)
LOOP
BEGIN
v_aud := 'AUDIT_'||r_table.table_name;
v_sql :=
'create table '
|| v_aud
|| ' as select * from '
|| r_table.owner
|| '.'
|| r_table.table_name
|| ' where 1 = 1';
DBMS_OUTPUT.put_line ('Info: ' || v_sql);
EXECUTE IMMEDIATE v_sql;
v_sql :=
'alter table '
|| v_aud
|| ' add ( AUDIT_ACTION char(1), AUDIT_BY varchar2(50), AUDIT_AT TIMESTAMP)';
EXECUTE IMMEDIATE v_sql;
v_count := c_tables%ROWCOUNT;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Failed to create table '
|| v_aud
|| ' due to '
|| SQLERRM);
END;
END LOOP;
IF v_count = 0
THEN
DBMS_OUTPUT.put_line ('No audit tables created');
ELSE
DBMS_OUTPUT.put_line (v_count || ' audit tables created.');
END IF;
END;
/
I checked it with show errors function and it gave me this:
LINE/COL ERROR
-------- --------------------------------------------
6/1 PL/SQL: SQL Statement ignored
16/3 PL/SQL: ORA-00906: missing left parenthesis
Perhaps the problem is quite simple, but I can't see it, so please help me if you can
I can compile procedure without problems after creating table
create table EXAUDIT (tname varchar2(100));
Check status of procedure:
select status from user_objects where lower(object_name) = 'create_audit_tables';