Oracle PL/SQL: Help resolving "PLS-00103: Encountered the symbol "LOOP" when expecting one of the following: if" - oracle

See Second Edit. New coding gets different error.
I am getting the "encountered a loop when expecting an if" error. My goal is to return a list of tables that have been updated by a specific project_id. The project_id is stored in a column on each table labeled project_id.
I used a with statement to create 2 tables. One table (sb_table) is a single column of all the tables that I want to look at. There are many more tables, but I figured it would speed things up if I narrowed down the list first. The other table I am creating (project) returns a single value turning the prjt_name provided into the actual project number (don't ask me why, but this is how my company has it set up, user creates a prct_name and is never aware of the project number).
Then I am trying to loop through the tables to see if they have the project number in their project_id column. If they do not, I delete them from the sb_table.
Ultimately, I am going to want to get all of the updated rows from all of the updated tables, but I am currently stuck on getting a list of the updated tables.
declare
query varchar2(10000);
table_count NUMBER;
update_count number;
prjt_name varchar2 not null := "01213264B";
cursor my_cur is select sbt.table_name from sb_table sbt;
begin
with sb_tables as (select table_name from all_tab_columns#db2 where
column_name = 'PROJECT_ID' and owner = 'SANDBOX'),
project as (select project_id from sandbox.sb_project#db2 where
project_name = upper(prjt_name))
--select sbt.table_name
--from sb_table sbt
for tableName in my_cur loop
query := 'select count(t.project_id) as "CNT" '||
'from sandbox.' || tableName || '#db2 t, project p '||
' where t.project_id = p.project_id ';
Execute immediate query
into update_count;
if update_count <= 0 then
query := 'DELETE FROM sb_tables where table_name = ' || tableName;
execute immediate query ;
end loop;
end;
Edit 1: Per comments, moved the select statement to the declare and am looping through my_cur now. I still get the same error.
Edit 2: Updated coding based on suggestions. I now get a different error message.
NEW ERROR: ORA-06550: line 12, column 16:
PLS-00306: wrong number or types of arguments in call to '||'
ORA-06550: line 12, column 7:
Edit 3: I was missing an = in my sub query which produced the error "Missing expression at line 13."
Edit 4: Now I get some results then error out with the following message
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-04040: file ext_qsp_benefit.dat in DATA_DIR not found
ORA-02063: preceding 3 lines from ADHOC_POS15
ORA-06512: at line 13
Edit 5: Success! Apparently I cannot query certain tables. So I just took those tables out.
Final coding is:
declare
query varchar2(10000);
update_count integer := 0;
prjt_name varchar2(100) := '01213264B';
cursor my_cur is (select table_name from all_tab_columns#db2 where column_name = 'PROJECT_ID' and owner = 'SANDBOX' and table_name in ('X') );
tableName varchar2(100);
begin
open my_cur;
loop
fetch my_cur into tableName;
exit when my_cur%NOTFOUND;
update_count := 0;
execute immediate
'select count(project_id) as "CNT" from sandbox.' || tableName || '#db2 '
|| ' where project_id = (select project_id from sandbox.sb_project#db2 where project_name = ''' || prjt_name || ''' ) '
into update_count;
if update_count > 0 then
dbms_output.put_line (tableName);
end if;
end loop;
close my_cur;
end;
This doesn't do exactly what I wanted. It sends the results to dbms_output. But It is a start! Thanks everyone for you help!

DECLARE
update_count integer := 0;
prjt_name varchar2(100) := 'tttt';
mysql varchar2(100);
tablename varchar2(100);
cursor my_cur is
select 'DUAL'
from dual
where 'PROJECT_ID' = 'PROJECT_ID' and 'SANDBOX' = 'SANDBOX';
begin
open my_cur;
LOOP
FETCH my_cur into tablename;
EXIT WHEN my_cur%NOTFOUND;
update_count := 0;
mysql := 'select count(*) ' || ' from '
|| tablename
|| ' where ''TTTT'' = upper('''
|| prjt_name
|| ''')' ;
Execute immediate mysql INTO update_count;
dbms_output.put_line (mysql);
dbms_output.put_line (update_count);
END LOOP;
CLOSE my_cur;
end;
I tried one like yours. This executes successfully
declare
query varchar2(10000);
update_count integer := 0;
prjt_name varchar2(100) := '01213264B';
cursor my_cur is
select table_name
from all_tab_columns#adhoc_pos15
where column_name = 'PROJECT_ID' and owner = 'SANDBOX';
tableName varchar2(100);
begin
open my_cur;
loop
fetch my_cur into tableName;
exit when my_cur%NOTFOUND;
update_count := 0;
query := 'select count(t.project_id) as ''CNT'' from sandbox.'
|| tableName
|| '#adhoc_pos15 t'
|| ' where t.project_id = (select project_id from sandbox.sb_project#adhoc_pos15 where project_name = ''' || prjt_name || ''') ' ;
execute immediate query into update_count;
dbms_output.put_line (query);
dbms_output.put_line (update_count);
end loop;
close my_cur;
end;

i have tried your code with some execution, but before that you need to correct your with clause query. below code has executed except with clause so, make some change dependence on your requirements.
**code :**
declare
query varchar2(10000);
table_count NUMBER;
update_count number;
prjt_name varchar2 not null := '01213264B';
cursor my_cur is
select sbt.table_name from sb_table sbt;
begin
/* with sb_tables as (select table_name from all_tab_columns#db2 where
column_name = 'PROJECT_ID' and owner = 'SANDBOX'),
project as (select project_id from sandbox.sb_project#db2 where
project_name = upper(prjt_name))*/
for tableName in my_cur
loop
query := 'select count(t.project_id) into '|| update_count || 'from sandbox.' || tableName || '#db2 t, project p '||' where t.project_id = p.project_id ';
Execute immediate query;
--into update_count;
if update_count <= 0 then
query := 'DELETE FROM sb_tables where table_name = ' || tableName;
execute immediate query ;
end if;
end loop;
end;

If you run it in sql-developer please note, there are different buttons for running query (green triangle) and running script (green smaller triangle over a white document). Use 'run script' button

Related

how to provide multiple bind variable for execute immediate inside pl/sql block

I have a table which contains the metadata of the table. The task is to periodically delete a specific set of tables, provided the information for where condition and how many days the data is retained are present.If a user needs to delete a data on daily basis, he simply enter his table name in audit log. The procedure will do the rest. The example is shown below.
Table structure:
CREATE TABLE delete_tbl_list (
id NUMBER NOT NULL,
table_name VARCHAR2(100) NOT NULL,
column_name VARCHAR2(100) NOT NULL,
day_retented NUMBER NOT NULL,
where_clause VARCHAR2(2000)
);
the day_retended is the number which will tell on how many days the data can hold.
select * from delete_tbl_list
ID TABLE_NAME COLUMN_NAME DAY_RETENTED WHERE_CLAUSE
---------- -----------------------------------------------
1 audit_log log_TS 60
So if i need to delete a table taking log_ts(timestamp) as column with 60days period as retention. The table in query needs to do
delete * from audit_log where log_ts<systimestamp -60
Now i need to do it using bulk delete and more dynamic and hence i wrote the procedure below,
create procedure dynamic_mass_delete as
TYPE tbl_rec_rowid IS TABLE OF ROWID INDEX BY PLS_INTEGER;
lv_del_exec_rec tbl_rec_rowid;
v_limit PLS_INTEGER := 10000;
m_date date:=sysdate;
total_records_deleted number:=0;
l_where delete_tbl_list.where_clause%type;
l_sql varchar2(2000);
TYPE ref_cur_type IS REF CURSOR;
delete_content ref_cur_type;
BEGIN
for i in (select table_name,COLUMN_NAME,DAY_RETENTED,WHERE_CLAUSE from delete_tbl_list) loop
DBMS_OUTPUT.PUT_LINE('tablename..'||i.table_name);
l_where:='';
m_date:=m_date-i.day_retented;
if i.where_clause is not null then
l_where:=' and '||i.where_clause;
end if;
OPEN delete_content FOR 'SELECT rowid from ' || i.table_name ||' where '|| i.COLUMN_NAME || ' <= to_timestamp('''||m_date||''')'||l_where;
LOOP
total_records_deleted := 0;
FETCH delete_content BULK COLLECT INTO lv_del_exec_rec LIMIT v_limit;
FORALL j IN lv_del_exec_rec.first..lv_del_exec_rec.last
execute immediate 'DELETE FROM :1 where rowid=:2 'using i.table_name,lv_del_exec_rec(j);
total_records_deleted := total_records_deleted + SQL%rowcount;
DBMS_OUTPUT.PUT_LINE('Delete count..'||total_records_deleted);
EXIT WHEN delete_content%notfound;
END LOOP;
CLOSE delete_content;
end loop;
EXCEPTION
when others then
DBMS_OUTPUT.PUT_LINE('Error-->'||SQLERRM);
END;
/
Now i getting error in the delete query stating invalid table name, i was not able to write dbms_output inside for all statment. Is it possible to use multiple bind variable inside a pl/sql procedure.
The error which i get is ,
Error-->ORA-00942: table or view does not exist
PL/SQL procedure successfully completed.
The table very much exists, but it is throwing error, i was not able to print inside the forall block too.
Switch to
execute immediate 'DELETE FROM ' || i.table_name ||' where rowid = ' || lv_del_exec_rec(j);
You can simplify your code as follows:
BEGIN
FOR TBL_DETAILS IN (
SELECT
TABLE_NAME,
COLUMN_NAME,
DAY_RETENTED,
WHERE_CLAUSE
FROM
DELETE_TBL_LIST
) LOOP
DBMS_OUTPUT.PUT_LINE('tablename..' || TBL_DETAILS.TABLE_NAME);
EXECUTE IMMEDIATE 'DELETE FROM '
|| TBL_DETAILS.TABLE_NAME
|| ' WHERE '
|| TBL_DETAILS.COLUMN_NAME
|| ' < SYSTIMESTAMP - '
|| TBL_DETAILS.DAY_RETENTED
|| CASE
WHEN TBL_DETAILS.WHERE_CLAUSE IS NOT NULL THEN ' AND ' || TBL_DETAILS.WHERE_CLAUSE
END;
DBMS_OUTPUT.PUT_LINE('Delete count..' || SQL%ROWCOUNT);
END LOOP;
END;
/
Hope, This will help you in creating simpler code.
Cheers!!

Native Dynamic sql,Execute immediate

EXECUTE immediate 'SELECT COUNT(*) INTO var_total_rows FROM '||v_table_name || ' where ENQUIRY_NO = :enq_no'
--INTO var_total_rows USING enq_no;
this is giving error of missing keyword . I am trying to count the number of rows fetched after passing the enquiry number for each table . approx 68 tables are in the output
thanks
You can try this way:
DECLARE
v_table_name VARCHAR2 (20) := 'emp';
enq_no NUMBER := 5;
var_total_rows NUMBER;
BEGIN
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' || v_table_name || ' where EMPNO = :enq_no'
INTO var_total_rows
USING enq_no;
DBMS_OUTPUT.put_line (var_total_rows);
END;
Edit:
This works for me. I just replaced ENQUIRY_NO with EMPLOYEE_ID since my schema doesnot have that table. Please make sure to assign value to variable enq_no
declare
var_total_rows number;
enq_no number:=1;
BEGIN
FOR c1 IN (SELECT DISTINCT table_name
FROM user_tab_columns
WHERE column_name = 'EMPLOYEE_ID' ) --'ENQUIRY_NO')
LOOP
DBMS_OUTPUT.PUT_LINE (' ');
DBMS_OUTPUT.PUT_LINE ('Table = ' || c1.table_name);
DBMS_OUTPUT.PUT_LINE ('=========================');
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '
|| c1.table_name
|| ' where EMPLOYEE_ID = :enq_no'
INTO var_total_rows
USING enq_no;
DBMS_OUTPUT.PUT_LINE ('total count:' || var_total_rows);
END LOOP;
END;
Output:
Table = EMPLOYEE
=========================
total count:1
Table = BONUSES
=========================
total count:1
Table = BONUSESS
=========================
total count:0

Oracle: Missing Expression

I have this set of code, it didn't return error when I compiled it.
create or replace PROCEDURE PARAM_CHECK
(
PARAM_CODE IN VARCHAR2,
TABLE_NAME IN VARCHAR2,
ISEXIST OUT INTEGER
)
IS
SQLSTMT VARCHAR2(200);
V_COUNT NUMBER;
BEGIN
SQLSTMT := 'SELECT COUNT(*) INTO '|| V_COUNT ||' FROM '|| TABLE_NAME ||' WHERE ID = '''||PARAM_CODE||''' AND ROWNUM = 1';
EXECUTE IMMEDIATE SQLSTMT;
COMMIT;
IF
V_COUNT = 0 THEN
ISEXIST := 0;
ELSE
ISEXIST := 1;
END IF;
END PARAM_CHECK;
But when after I try to run the procedure, it return this error:
Connecting to the database SAA.
ORA-00936: missing expression
ORA-06512: at "SAA.PARAM_CHECK", line 12
ORA-06512: at line 9
Process exited.
Disconnecting from the database SAA.
I have no idea why it return this error, plus when I compile everything is okay. I did search for the specific ORA error but didn't help much. Please help me. Thanks in advance.
Vcount is uninitialised at the time of the inclusion of its value into the sql statement so it produces a sytactically wrong blank at this position.
If it was feasible at all you'd have to write
SQLSTMT := 'SELECT COUNT(*) INTO V_COUNT FROM '|| TABLE_NAME ||' WHERE ID = '''||PARAM_CODE||''' AND ROWNUM = 1';
but this won't work either iirc since execute immediate opens a new scope.
You have to resort to
SQLSTMT := 'SELECT COUNT(*) FROM '|| TABLE_NAME ||' WHERE ID = '''||PARAM_CODE||''' AND ROWNUM = 1';
EXecute immediate sqlstmt into v_count;

Dynamically assigning variables oracle sql

I have a table attribute_config with below columns:
table_name column_name key
Let us say is has below 2 rows
account accountphone accountnum
customer customernumber customerid
Key can be only accountnum or customerid.
I have to write code which will accept (i_accountnum,i_customerid) and;
fetch the respective values from columns mentioned in column_name in tables mentioned in table_name using the key in where condition.
For ex: select accountphone from account where accountnum = i_accountnum
select customernumber from customer where customerid = i_customerid
the complete query should be formed dynamically, whether to pass i_accountnum or i_customerid in the query also needs to be decided dynamically. if key - accountnum, i_accountnum will be passed to where condition.
I have been trying on these lines so far, this is not working, i know it is wrong.
declare
v_accountnum varchar2(20);
v_customerid varchar2(20);
v_attribute_value varchar2(20);
v_stmt varchar2(255);
begin
Account_Num := 'TestCustomer'; -- input to the function
v_customer_ref := 'TestAccount'; -- input to the function
for i in (Select * from attribute_config) loop
v_stmt := 'select ' || i.column_name || ' from ' || i.table_name ||' where ' || i.key|| ' = v_' || i.key;
execute immediate v_Stmt into v_attribute_value;
end loop;
end;
This will fix your code, but I do not see any advantage of using dynamic query when your code should accept 2 parameters(i_accountnum,i_customerid) - which is already static situation and fetch the relevant values, perhaps only in learning purposes.
declare
procedure fecth_values(i_accountnum account.accountnum%type,
i_customerid customer.customerid%type) return varchar2 is
v_attribute_value varchar2(20);
begin
for i in (select * from attribute_config) loop
execute immediate 'select ' || i.column_name || ' from ' ||
i.table_name || ' where ' || i.key || ' = ' || case when i.key = 'accountnum' then i_accountnum when i.key = 'customerid' then i_customerid end;
into v_attribute_value;
dbms_output.put_line(v_attribute_value);
end loop;
return null;
end;
begin
fecth_values(1, 1);
end;
Your where clause was wrong the i.key should be compared against the inputed values, not the 'v_' || i.key, which is undeclared when you execute your stmt.

Execute For Each Table in PLSQL

I want to the the number of records in all tables that match a specific name criteria. Here is the SQL I built
Declare SQLStatement VARCHAR (8000) :='';
BEGIN
SELECT 'SELECT COUNT (*) FROM ' || Table_Name || ';'
INTO SQLStatement
FROM All_Tables
WHERE 1=1
AND UPPER (Table_Name) LIKE UPPER ('MSRS%');
IF SQLStatement <> '' THEN
EXECUTE IMMEDIATE SQLStatement;
END IF;
END;
/
But I get the following error:
Error at line 1
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 3
Script Terminated on line 1.
How do I modify this so that it runs for all matching tables?
Update:
Based on an answer received, I tried the following but I do not get anything in the DBMS_OUTPUT
declare
cnt number;
begin
for r in (select table_name from all_tables) loop
dbms_output.put_line('select count(*) from CDR.' || r.table_name);
end loop;
end;
/
declare
cnt number;
begin
for r in (select owner, table_name from all_tables
where upper(table_name) like ('%MSRS%')) loop
execute immediate 'select count(*) from "'
|| r.owner || '"."'
|| r.table_name || '"'
into cnt;
dbms_output.put_line(r.owner || '.' || r.table_name || ': ' || cnt);
end loop;
end;
/
If you're selecting from all_tables you cannot count on having been given the grants necessary to select from the table name. You should therefore check for the ORA-00942: table or view does not exist error thrown.
As to the cause for your error: You get this error because the select statement returns a result set with more than one row (one for each table) and you cannot assign such a result set to a varchar2.
By the way, make sure you enable dbms_output with SET SERVEROUT ON before executing this block.

Resources