EXECUTE IMMEDIATE DROP FUNCTION causes Oracle SQL Developer to hang - sql

I have the following script. But whenever I include the line EXECUTE IMMEDIATE 'DROP FUNCTION table_exists';, SQL Developer seems to hang. Any idea what could be causing it?
If I cancel the task, the script runs to completion but doesn't drop the function. However, I can simply run DROP FUNCTION table_exists; in my DB connection and it drops the table no problem. Is it some quirk of PL/SQL?
CREATE OR REPLACE FUNCTION table_exists(table_name VARCHAR2)
RETURN BOOLEAN
AS
table_count NUMBER := 0;
exists_sql VARCHAR2(255);
BEGIN
exists_sql := 'SELECT COUNT(1) FROM tab WHERE tname = :tab_name';
EXECUTE IMMEDIATE exists_sql INTO table_count USING table_name;
RETURN table_count > 0;
END;
/
DECLARE
TYPE tables_array IS VARRAY(2) OF VARCHAR2(25); -- change varray size if running on dev
tables tables_array;
BEGIN
tables := tables_array(
'FOO',
'BAR'
);
FOR table_element IN 1 .. tables.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('Backing up data for ' || tables(table_element));
IF table_exists(tables(table_element) || '_ORIGINAL') THEN
DBMS_OUTPUT.PUT_LINE(tables(table_element) || '_ORIGINAL already exists');
ELSE
EXECUTE IMMEDIATE 'CREATE TABLE ' || tables(table_element) || '_ORIGINAL AS SELECT * FROM '|| tables(table_element);
END IF;
END LOOP;
EXECUTE IMMEDIATE 'DROP FUNCTION table_exists';
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('Unexpected error: ' || sqlerrm);
ROLLBACK;
RAISE;
END;
/

Oracle won't drop a function which is being used in this very procedure.
Is there a workaround? Yes, at least one - submit a job which will do it in another session.
Sample tables (so that procedure won't fail because of that)
SQL> create table foo (id number);
Table created.
SQL> create table bar (id number);
Table created.
Create a function:
SQL> CREATE OR REPLACE FUNCTION table_exists(table_name VARCHAR2)
2 RETURN BOOLEAN
3 AS
4 table_count NUMBER := 0;
5 exists_sql VARCHAR2(255);
6 BEGIN
7 exists_sql := 'SELECT COUNT(1) FROM tab WHERE tname = :tab_name';
8 EXECUTE IMMEDIATE exists_sql INTO table_count USING table_name;
9
10 RETURN table_count > 0;
11 END;
12 /
Function created.
Is it here? Yes, it is:
SQL> desc table_Exists;
FUNCTION table_Exists RETURNS BOOLEAN
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
TABLE_NAME VARCHAR2 IN
Run the main piece of code; note line #4 (declaration of a variable which will contain job number) and line #21 which submits a job.
SQL> DECLARE
2 TYPE tables_array IS VARRAY(2) OF VARCHAR2(25); -- change varray size if running on dev
3 tables tables_array;
4 i number; -- job number
5 BEGIN
6 tables := tables_array(
7 'FOO',
8 'BAR'
9 );
10
11 FOR table_element IN 1 .. tables.COUNT LOOP
12 DBMS_OUTPUT.PUT_LINE('Backing up data for ' || tables(table_element));
13 IF table_exists(tables(table_element) || '_ORIGINAL') THEN
14 DBMS_OUTPUT.PUT_LINE(tables(table_element) || '_ORIGINAL already exists');
15 ELSE
16 EXECUTE IMMEDIATE 'CREATE TABLE ' || tables(table_element) || '_ORIGINAL AS SELECT * FROM '|| tables(table_element);
17 END IF;
18 END LOOP;
19
20 -- EXECUTE IMMEDIATE 'DROP FUNCTION table_exists';
21 dbms_job.submit(i, 'begin execute immediate ''drop function table_exists''; end;');
22
23 COMMIT;
24
25 EXCEPTION
26 WHEN OTHERS THEN
27 DBMS_OUTPUT.PUT_LINE ('Unexpected error: ' || sqlerrm);
28 ROLLBACK;
29 RAISE;
30 END;
31 /
PL/SQL procedure successfully completed.
Is the function still here? Nope, it was dropped.
SQL> desc table_exists;
ERROR:
ORA-04043: object table_exists does not exist
SQL>

Related

How to drop and create table again and again

when i use this code first time ,it create the table but i want to use this code again and again but second time when i use it ,it will drop the table but does not create table again . kindly help me to correct the code according to my requirement.
set serverout on
DECLARE
table_or_view_does_not_exist exception;
pragma exception_init(table_or_view_does_not_exist,-00942);
ddl_qry VARCHAR2 (200);
ddl_table varchar2(200);
r_emp SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST();
v_array SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST('ACCT_ID',
'PARENT_ACCT_ID',
'CUST_ID',
'ACCT_NAME',
'BILLING_CYCLE_TYPE',
'PAID_FLAG',
'BILL_DELIVER_METHOD');
BEGIN
ddl_qry:='Drop Table Accnt_Profile_Spcl';
EXECUTE IMMEDIATE ddl_qry;
exception
when table_or_view_does_not_exist then
dbms_output.put_line('There is no error');
GOTO end_point;
<<end_point>>
ddl_table := 'create table Accnt_Profile_Spcl(
column_name varchar2(50),
spcl_char_count number)';
EXECUTE IMMEDIATE ddl_table;
dbms_output.put_line('Table has been created');
---------DBMS_OUTPUT.ENABLE;
FOR i IN 1..v_array.COUNT LOOP
r_emp.EXTEND;
EXECUTE IMMEDIATE
'SELECT /*+parallel(16)*/ COUNT(*) FROM account_profile WHERE NOT REGEXP_LIKE('||v_array(i)||',''[A-Za-z0-9.]'')'
INTO r_emp(i);
if r_emp(i)<>0 then
-----------dbms_output.put_line(v_array(i) || ': ' || r_emp(i));
execute immediate 'insert into Accnt_Profile_Spcl values (:param1,:param2)' using v_array(i), r_emp(i);
end if;
END LOOP;
END;
That's bad practice. You should really avoid dropping and creating tables that way, that's not how Oracle is supposed to work. Create table once, at SQL level, and reuse it as many times as you want. Just remove its contents, if you want / have to.
Anyway: here's how you could do that:
SQL> declare
2 l_cnt number;
3 begin
4 select count(*) into l_cnt
5 from user_tables
6 where table_name = 'ACCNT_PROFILE_SPCL';
7
8 if l_cnt = 1 then
9 execute immediate 'drop table ACCNT_PROFILE_SPCL';
10
11 dbms_output.put_line('Table dropped');
12 end if;
13
14 execute immediate 'create table ACCNT_PROFILE_SPCL ' ||
15 ' (column_name varchar2(50),' ||
16 ' spcl_char_count number)';
17
18 dbms_output.put_line('Table created');
19 end;
20 /
Table dropped
Table created
PL/SQL procedure successfully completed.
SQL> /
Table dropped
Table created
PL/SQL procedure successfully completed.
SQL>
Use a nested block to handle the exception and not the main block:
DECLARE
ddl_qry VARCHAR2 (200);
ddl_table varchar2(200);
r_emp SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST();
v_array SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST('ACCT_ID',
'PARENT_ACCT_ID',
'CUST_ID',
'ACCT_NAME',
'BILLING_CYCLE_TYPE',
'PAID_FLAG',
'BILL_DELIVER_METHOD');
BEGIN
DECLARE
table_or_view_does_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT(table_or_view_does_not_exist,-00942);
BEGIN
ddl_qry:='Drop Table Accnt_Profile_Spcl';
EXECUTE IMMEDIATE ddl_qry;
EXCEPTION
WHEN table_or_view_does_not_exist THEN
dbms_output.put_line('There is no error');
END;
ddl_table := 'create table Accnt_Profile_Spcl(
column_name varchar2(50),
spcl_char_count number)';
EXECUTE IMMEDIATE ddl_table;
dbms_output.put_line('Table has been created');
FOR i IN 1..v_array.COUNT LOOP
r_emp.EXTEND;
EXECUTE IMMEDIATE
'SELECT /*+parallel(16)*/ COUNT(*) FROM account_profile WHERE NOT REGEXP_LIKE('||v_array(i)||',''[A-Za-z0-9.]'')'
INTO r_emp(i);
if r_emp(i)<>0 then
execute immediate 'insert into Accnt_Profile_Spcl values (:param1,:param2)' using v_array(i), r_emp(i);
end if;
END LOOP;
END;
/
fiddle

How to write a PL/SQL procedure with an Instead Of Trigger as Dynamic SQL?

I wrote a PL/SQL function and am using dynamic SQL to execute a Create trigger statement as follows :-
CREATE OR REPLACE FUNCTION register_driver1(driver_name IN VARCHAR, pass_word IN VARCHAR) RETURN NUMBER AS
sql_stmt VARCHAR2(500);
driver_id NUMBER;
new_view_name VARCHAR(50); --<-----Line 4
BEGIN
--function statements
sql_stmt := 'CREATE OR REPLACE TRIGGER reg_vehicle
INSTEAD OF INSERT ON '||new_view_name||
' FOR EACH ROW
DECLARE
vehicle_id NUMBER;
BEGIN
vehicle_id := vehicle_ids.nextval
INSERT INTO Vehicles VALUES(:NEW.Model, :NEW.Seats, :NEW.reg_no, vehicle_id, '||driver_id||');
END;';
EXECUTE IMMEDIATE sql_stmt; --<-----Line 32
--Remaining function body
END;
/
Here, the variables new_view_name, driver_id are defined above this code snippet. Vehicle is a table(Model, Seats, Reg_no, vehicel_id, driver_id) and reg_vehicle(Model, Seats, Reg_no) is a view having Vehicles of a particular driver_id.
vehicle_ids is a sequence created outside the procedure.
The above shows compilation error at the EXECUTE IMMEDIATE line. What is the correct way to do this?
The error shown when function is called with some driver_name and pass_word:-
ORA-24344: success with compilation error ORA-06512: at "ADMIN.REGISTER_DRIVER1", line 32 ORA-06512: at line 4
Well, I wouldn't recommend creating objects dynamically.
If you - for some reason - insist on that, then another problem: you can't perform DDL here as you'd call a function as e.g.
select register_driver1(...) from dual;
and get
ORA-14552: cannot perform a DDL, commit or rollback inside a query or DML
So - switch to a procedure with an OUT parameter to return whatever that function is supposed to return.
As of error you got: you miss BEGIN. Something like this compiles:
SQL> CREATE OR REPLACE PROCEDURE register_driver1 (driver_name IN VARCHAR,
2 pass_word IN VARCHAR)
3 AS
4 sql_stmt VARCHAR2 (500);
5 driver_id NUMBER;
6 new_view_name VARCHAR (50);
7 BEGIN
8 sql_stmt :=
9 'CREATE OR REPLACE TRIGGER reg_vehicle
10 INSTEAD OF INSERT ON '
11 || new_view_name
12 || ' FOR EACH ROW
13 DECLARE
14 vehicle_id NUMBER;
15 BEGIN
16 vehicle_id := vehicle_ids.nextval
17 INSERT INTO Vehicles VALUES(:NEW.Model, :NEW.Seats, :NEW.reg_no, vehicle_id, '
18 || driver_id
19 || ');
20 END;';
21
22 -- EXECUTE IMMEDIATE sql_stmt;
23 DBMS_OUTPUT.put_line (sql_stmt);
24 END;
25 /
Procedure created.
SQL>
SQL> SET SERVEROUTPUT ON;
SQL> EXEC register_driver1('a', 'b');
CREATE OR REPLACE TRIGGER reg_vehicle
INSTEAD OF INSERT ON FOR
EACH ROW
DECLARE
vehicle_id NUMBER;
BEGIN
vehicle_id := vehicle_ids.nextval
INSERT
INTO Vehicles VALUES(:NEW.Model, :NEW.Seats, :NEW.reg_no, vehicle_id, );
END;
PL/SQL procedure successfully completed.
SQL>

Assigning multiple fields in a loop using execute immediate

I am using PLPDF's libraries to create spreadsheets for various files - I am trying to get a procedure written to take the values of each field and one-by-one insert them into the spreadsheet. This operation can include many different tables going into many different spreadsheets, so just doing an export is not going to cut it.
This example is has two cursors created from tables - the USER_TAB_COLUMNS to select the column names, and the actual view query to pull the data. I have one loop to go through the data record by record, and second to go field-by-field within the record.
Before doing the actual writing to the spreadsheet blob, I'm simply running the dbms_output.putline to make sure that I am getting the data needed.
declare
q_str varchar2(100);
this_vc varchar2(3000);
cursor diet_table is
select * from vcars_diet where nhp_id = 8573;
cursor diet_stru is
select 'begin :1 := i.' || column_name || '; end;' line_o_code from user_tab_columns where table_name = 'VCARS_DIET';
begin
for i in diet_table loop
DBMS_OUTPUT.PUT_LINE ('--------------------------------------------------------');
for h in diet_stru loop
DBMS_OUTPUT.PUT_LINE ('Varchar Value for i: "' || h.line_o_code || '"');
EXECUTE IMMEDIATE (h.line_o_code) USING out this_vc;
DBMS_OUTPUT.PUT_LINE ('Varchar Value for i.' || h.line_o_code || ' is: '||this_vc);
end loop;
end loop;
end;
The fields in the diet table are:
NHP_ID
DATE_TIME
DIET_NO
FORM_NAME
DATA_TYPE
The results are:
ORA-06550: line 1, column 13:
PLS-00201: identifier 'I.NHP_ID' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
ORA-06512: at line 33
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I have copied this PL/SQL code from Connor McDonald's solution from this AskTom article.
It uses type dynamic SQL to parse any SQL query and converts the column names as well as values into a collection. I've used a sample query on employees table in HR schema. Replace it with your query.
set serverout on size 999999
set verify off
declare
p_query varchar2(32767) :=
q'{select * from employees
where rownum = 1
}';-- Here you put your query
l_theCursor integer default dbms_sql.open_cursor;
l_columnValue varchar2(4000);
l_status integer;
l_descTbl dbms_sql.desc_tab;
l_colCnt number;
n number := 0;
procedure p(msg varchar2) is
l varchar2(4000) := msg;
begin
while length(l) > 0 loop
dbms_output.put_line(substr(l,1,80));
l := substr(l,81);
end loop;
end;
begin
execute immediate
'alter session set nls_date_format=''dd-mon-yyyy hh24:mi:ss'' ';
dbms_sql.parse( l_theCursor, p_query, dbms_sql.native );
dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl );
for i in 1 .. l_colCnt loop
dbms_sql.define_column(l_theCursor, i, l_columnValue, 4000);
end loop;
l_status := dbms_sql.execute(l_theCursor);
while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
for i in 1 .. l_colCnt loop
dbms_sql.column_value( l_theCursor, i, l_columnValue );
p( 'Value for '|| l_descTbl(i).col_name
|| ' is: ' ||
l_columnValue );
end loop;
dbms_output.put_line( '-----------------' );
n := n + 1;
end loop;
if n = 0 then
dbms_output.put_line( chr(10)||'No data found '||chr(10) );
end if;
end;
/
This gives the output:
Value for EMPLOYEE_ID is: 198
Value for FIRST_NAME is: Donald
Value for LAST_NAME is: OConnell
Value for EMAIL is: DOCONNEL
Value for PHONE_NUMBER is: 650.507.9833
Value for HIRE_DATE is: 21-jun-2007 00:00:00
Value for JOB_ID is: SH_CLERK
Value for SALARY is: 2600
Value for COMMISSION_PCT is:
Value for MANAGER_ID is: 124
Value for DEPARTMENT_ID is: 50
-----------------
PL/SQL procedure successfully completed.

How do I run a count of rows over a database link?

This code, works. It runs a row count the way you'd expect, I want to tweek it, mostly to do a count over a db_link for tables dictated as I see fit.
declare
n number;
begin
for i in (select table_name from user_tables) loop
execute immediate' select count(*) from '||i.table_name into n;
dbms_output.put_line('Table Name: '||i.table_name||' Count of Row''s: '||n);
end loop;
end;
/
So, this is the adapted code... it includes a variable with the name of the link. (The link works fine) But how to reference it is probably where I'm coming unstuck.
declare
l_dblink varchar2(100) := 'DB1';
n number;
begin
for i in (select table_name from my_tables) loop
execute immediate' select count(*) from '||i.table_name#||l_dblink into n;
dbms_output.put_line('Table Name: '||i.table_name||' Count of Row''s: '||n);
end loop;
end;
/
Can someone please have a look and tell me where I'm going wrong? I just want the SQL to pick up the table names from a local table, and then use the names to count the rows in those tables, which reside in the remote database.
Monkey is on the wrong tree and can't eat a banana.
SQL> create table my_tables (table_name varchar2(20));
Table created.
SQL> insert into my_tables values ('dual');
1 row created.
SQL> set serveroutput on
SQL> declare
2 l_dblink varchar2(100) := 'db1';
3 n number;
4 begin
5 for i in (select table_name from my_tables) -- has to be like this
6 loop -- vvv
7 execute immediate' select count(*) from '||i.table_name || '#' || l_dblink into n;
8 dbms_output.put_line('Table Name: '||i.table_name||' Count of Row''s: '||n);
9 end loop;
10 end;
11 /
Table Name: dual Count of Row's: 1
PL/SQL procedure successfully completed.

How do you specify IN clause in a dynamic query using a variable?

In PL/SQL, you can specify the values for the IN operator using concatenation:
v_sql := 'select field1
from table1
where field2 in (' || v_list || ')';
Is it possible to do the same using a variable?
v_sql := 'select field1
from table1
where field2 in (:v_list)';
If so, how?
EDIT: With reference to Marcin's answer, how do I select from the resultant table?
declare
cursor c_get_csv_as_tables is
select in_list(food_list) food_list
from emp_food
where emp_type = 'PERM';
cursor c_get_food_list (v_food_table varchar2Table)is
select *
from v_food_table;
begin
for i in c_get_csv_as_tables loop
for j in c_get_food_list(i.food_list) loop
dbms_output.put_line(j.element);
end loop;
end loop;
end;
I get the following error:
ORA-06550: line 10, column 6:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 9, column 1:
PL/SQL: SQL Statement ignored
ORA-06550: line 15, column 34:
PLS-00364: loop index variable 'J' use is invalid
ORA-06550: line 15, column 13:
PL/SQL: Statement ignored
Like in #Sathya link, you can bind the varray (I took #Codo example):
CREATE OR REPLACE TYPE str_tab_type IS VARRAY(10) OF VARCHAR2(200);
/
DECLARE
l_str_tab str_tab_type;
l_count NUMBER;
v_sql varchar2(3000);
BEGIN
l_str_tab := str_tab_type();
l_str_tab.extend(2);
l_str_tab(1) := 'TABLE';
l_str_tab(2) := 'INDEX';
v_sql := 'SELECT COUNT(*) FROM all_objects WHERE object_type IN (SELECT COLUMN_VALUE FROM TABLE(:v_list))';
execute immediate v_sql into l_count using l_str_tab;
dbms_output.put_line(l_count);
END;
/
UPDATE: the first command can be replaced with:
CREATE OR REPLACE TYPE str_tab_type IS TABLE OF VARCHAR2(200);
/
then call:
l_str_tab.extend(1);
when ever you add a value
Unfortunately you cannot bind a list like this, however you can use a table function. Read this
Here's an example of usage based on your code:
declare
cursor c_get_csv_as_tables is
select in_list(food_list) food_list
from emp_food
where emp_type = 'PERM';
cursor c_get_food_list (v_food_table varchar2Table)is
select column_value food
from TABLE(v_food_table);
begin
for i in c_get_csv_as_tables loop
for j in c_get_food_list(i.food_list) loop
dbms_output.put_line(j.food);
end loop;
end loop;
end;
I used here a column_value pseudocolumn
Bind variable can be used in Oracle SQL query with "in" clause.
Works in 10g; I don't know about other versions.
Bind variable is varchar up to 4000 characters.
Example: Bind variable containing comma-separated list of values, e.g.
:bindvar = 1,2,3,4,5
select * from mytable
where myfield in
(
SELECT regexp_substr(:bindvar,'[^,]+', 1, level) items
FROM dual
CONNECT BY regexp_substr(:bindvar, '[^,]+', 1, level) is not null
);
As per #Marcin's answer you can't do this, however, there's a fair bit to add to that, as your query should actually work, i.e. run.
Simply put, you cannot use a bind variable for a table or column. Not only that, bind variables they are assumed to be a character, so if you want a number you have to use to_number(:b1) etc.
This is where your query falls down. As you're passing in a string Oracle assumes that your entire list is a single string. Thus you are effectively running:
select field1
from table1
where field2 = v_list
There is no reason why you can't do this a different way though. I'm going to assume you're dynamically creating v_list, which means that all you need to do is create this list differently. A series of or conditions is, purportedly :-), no different to using an in.
By purportedly, I mean never rely on something that's untested. Although Tom does say in the link that there may be performance constraints there's no guarantee that it wasn't quicker than using in to begin with. The best thing to do is to run the trace on your query and his and see what difference there is, if any.
SQL> set serveroutput on
SQL>
SQL> declare
2
3 l_string varchar2(32767);
4 l_count number;
5
6 begin
7
8 for xx in ( select rownum as rnum, a.*
9 from user_tables a
10 where rownum < 20 ) loop
11
12 if xx.rnum = 1 then
13 l_string := 'table_name = ''' || xx.table_name || '''';
14 else
15 l_string := l_string || ' or table_name = ''' || xx.table_name || '
''';
16 end if;
17
18 end loop;
19
20 execute immediate 'select count(*)
21 from user_tables
22 where ' || l_string
23 into l_count
24 ;
25
26 dbms_output.put_line('count is ' || l_count);
27
28 end;
29 /
count is 19
PL/SQL procedure successfully completed.