Oracle procedure cursor loop errors - sql

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

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'

PLSQL procedure for automatic column type changing

Let's assume following table:
drop table test1;
create table test1(
A number(10)
);
insert into test1 values (1);
insert into test1 values (10);
So as you can see table TEST1 is already populated. What i need to do is to change types of column A to varchar2. Since this column has values we can't just use following code:
alter table test1 modify A varchar2(10);
So i have wrote stored procedure which:
Renames column A to A1 ->
Then adds new column called A of type varchar2 ->
Then updates column A with values from column A1 ->
And ultimately drops old column A1.
Code which runs this process is following:
create or replace procedure change_col_type_to_varchar2(p_tab in varchar2, p_col in varchar2)
is
v_string clob;
cursor cur is
select column_name
from all_tab_columns
where table_name = upper(p_tab) and
column_name in (select regexp_substr(p_col,'[^,]+', 1, level) from dual
connect by regexp_substr(p_col, '[^,]+', 1, level) is not null);
begin
for i in cur loop
v_string := 'alter table ' || p_tab || ' rename column ' || i.column_name || ' to ' || i.column_name || '1' || ';';
dbms_lob.append(v_string,''||chr(10)||'');
dbms_lob.append(v_string, 'alter table ' || p_tab || ' add ' || i.column_name || ' varchar2(10);');
dbms_lob.append(v_string,''||chr(10)||'');
dbms_lob.append(v_string, 'update ' || p_tab || ' set ' || i.column_name || ' = ' || i.column_name || '1' || ';');
dbms_lob.append(v_string,''||chr(10)||'');
dbms_lob.append(v_string, 'alter table ' || p_tab || ' drop column ' || i.column_name || '1' || ';');
EXECUTE IMMEDIATE v_string;
DBMS_OUTPUT.PUT_LINE(v_string);
v_string := NULL;
end loop;
end;
I'am trying to apply this procedure to TEST1:
begin
change_col_type_to_varchar2('TEST1', 'A');
end;
And get error:
Error report -
ORA-23290: This operation may not be combined with any other operation
ORA-06512: at "YAVORSKYIY_DM.CHANGE_COL_TYPE_TO_VARCHAR2", line 19
ORA-06512: at "YAVORSKYIY_DM.CHANGE_COL_TYPE_TO_VARCHAR2", line 19
ORA-06512: at line 2
23290. 00000 - "This operation may not be combined with any other operation"
*Cause: ALTER TABLE RENAME COLUMN/CONSTRAINT operation was given in
conjunction with another ALTER TBALE Operation. This is not
allowed.
*Action: Ensure that RENAME COLUMN/CONSTRAINT is the only operation
specified in the ALTER TABLE.
But just typing :
alter table test1 rename column A to A1;
alter table test1 add A varchar2(100);
update test1 set A = A1;
alter table test1 drop column A1;
Works perfect.
Does anybody have any ideas about how to overcome this problem?
Appreciate your help.
the below will do what you asked for.
declare
procedure change_col_type_to_varchar2(p_tab in varchar2, p_col in varchar2)
is
v_string clob;
cursor cur is
select column_name
from all_tab_columns
where table_name = upper(p_tab) and
column_name in (select regexp_substr(p_col,'[^,]+', 1, level)
from dual
connect by regexp_substr(p_col, '[^,]+', 1, level) is not null);
begin
for i in cur loop
v_string := 'alter table ' || p_tab || ' rename column ' || i.column_name || ' to ' || i.column_name || '1';
execute immediate v_string;
v_string := 'alter table ' || p_tab || ' add ' || i.column_name || ' varchar2(10)';
execute immediate v_string;
v_string := 'update ' || p_tab || ' set ' || i.column_name || ' = ' || i.column_name || '1' ;
execute immediate v_string;
v_string := 'alter table ' || p_tab || ' drop column ' || i.column_name || '1' ;
execute immediate v_string;
v_string := NULL;
end loop;
end;
begin
DBMS_OUTPUT.PUT_LINE('Before calling');
change_col_type_to_varchar2('TEST1','A');
DBMS_OUTPUT.PUT_LINE('After calling');
end;
Well, execute each statement alone, instead of concatenating them. And you don't need LOBs, varchar2 for each one should be enough.
I propose other algorithm.
create new table 'table2' with varchar column;
select all values from table1.A and insert to table2 with to_char() conversion;
drop table1;
rename table2 to table1;
profit!

Output of Duplicats using a Procedure

i am in the middle of studys and currently doing some exercises.
I am trying to make procedure that shows how many duplicats are in a row.
CREATE OR REPLACE PROCEDURE anzahl1(
tabelle VARCHAR2,
reihe VARCHAR2,
wieviel OUT NUMBER
)AS
test VARCHAR2(4000);
BEGIN
EXECUTE IMMEDIATE 'SELECT
reihe,
(COUNT(*)-1) AS Anzahl
INTO
wieviel
FROM
tabelle
GROUP BY
reihe
HAVING COUNT(*) > 1' using out wieviel;
DBMS_OUTPUT.PUT_LINE(wieviel);
END;
/
i tried many things but nothing worked...
even the result i was given by a teacher of my old school didnt work:
CREATE OR REPLACE PROCEDURE check_doppelte_Werte_p (
p_tabellenname IN USER_TAB_COLUMNS.TABLE_NAME%TYPE,
p_spaltenname IN USER_TAB_COLUMNS.COLUMN_NAME%TYPE,
p_ergebnis BOOLEAN
) OUT
IS
v_dummy NUMBER := 1;
v_sql_anweisung varchar2 (4000);
BEGIN
v_sql_anweisung :=
'SELECT MAX(COUNT ('
|| p_spaltenname
|| ')) '
|| ' FROM '
|| p_tabellenname
|| ' GROUP BY '
|| p_spaltenname;
DBMS_OUTPUT.PUT_LINE (v_sql_anweisung);
EXECUTE IMMEDIATE v_sql_anweisung INTO v_dummy;
IF v_dummy > 1 THEN
DBMS_OUTPUT.PUT_LINE( 'Die Tabelle '
|| 'hat mindestens '
|| TO_CHAR (V_DUMMY)
|| ' doppelte Werte in der Spalte '
|| p_spaltenname);
p_ergebnis := TRUE;
ELSE
p_ergebnis := FALSE;
END IF;
EXCEPTION
-- Keine Werte gefunden, da
WHEN NO_DATA_FOUND
THEN
p_ergebnis := FALSE;
DBMS_OUTPUT.PUT_LINE ('Tabellenname oder Spaltenname sind nicht vorhan-
den!!!');
END;
What would you do/change?
Your code doesn't substitute the variables in the dynamic sql.The query String has been to be appended with the input variables.
Please try the code below,
CREATE OR REPLACE PROCEDURE anzahl1(
tabelle VARCHAR2,
reihe VARCHAR2,
wieviel OUT NUMBER
)AS
test VARCHAR2(4000);
lv_query VARCHAR2(4000);
BEGIN
lv_query := 'SELECT (COUNT(*)-1) cnt FROM '||tabelle ||' GROUP BY '||reihe||' HAVING COUNT(*) > 1';
EXECUTE IMMEDIATE lv_query into wieviel;
DBMS_OUTPUT.PUT_LINE(wieviel);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR '||SQLCODE||' '||SUBSTR(SQLERRM,1,200));
END;
/
I create table and populate records as below,
CREATE TABLE EMP(ID NUMBER(10),NAME VARCHAR2(40));
INSERT INTO EMP VALUES (1, 'TEST');
INSERT INTO EMP VALUES (1, 'TEST');
INSERT INTO EMP VALUES (2, 'TEST2');
Now i call the stored procedure to test for EMP table for ID column,
declare
lv_cnt number(10);
begin
anzahl1('EMP','ID',lv_cnt);
dbms_output.put_line(lv_cnt);
end;
This give output as ,
1

00933. 00000 - "SQL command not properly ended" DYNAMIC SQL

DECLARE
CURSOR c_ddy (p_table_name VARCHAR2, p_table_name_2 VARCHAR2)
IS
(SELECT column_name
FROM ( SELECT column_name
FROM user_tab_columns
WHERE table_name = p_table_name
ORDER BY column_name));
TYPE t_content IS TABLE OF VARCHAR2 (9000)
INDEX BY BINARY_INTEGER;
a_content t_content;
l_sql VARCHAR2 (1000);
BEGIN
FOR r_ddy IN c_ddy ('TABLE_a', 'TABLE_B')
LOOP
l_sql :=
'SELECT '
|| r_ddy.column_name
|| ' FROM TABLE_A '
|| 'MINUS'
|| 'SELECT '
|| r_ddy.column_name
|| 'FROM TABLE_B';
EXECUTE IMMEDIATE l_sql BULK COLLECT INTO a_content;
DBMS_OUTPUT.put_line (l_sql);
END LOOP;
END;
Trying to output every column in TABLE_A minus every column in TABLE_B.
A space is missing in the SQL string.
Try this:
l_sql :=
'SELECT '
|| r_ddy.column_name
|| ' FROM TABLE_A '
|| ' MINUS '
|| ' SELECT '
|| r_ddy.column_name
|| ' FROM TABLE_B';
UPD: For some reason the spaces were gone before. Now I put them again.

How do i copy old columns to my new existing table?

I have an existing new table that has no columns yet. I want to copy all 10 columns in my old table. How do i do that? I don't want to drop the table so I could perform: create table newTable as select * from oldTable.
I don't want to drop the table so I could perform:
create table newTable as select * from oldTable.
Assuming your new table has at least 1 column BobC told you cannot have a table without any column, you can alter your table.
Alter new_table add (col1 varchar2(10)) , col2 ...);
Note that you need to mention all the column same as old table manually here.
If you don't want to do this manually then probably you would need a PLSQL block to do this.
CREATE OR REPLACE PROCEDURE creat_tbl_frm_tbl (tablname VARCHAR2)
AS
db_user VARCHAR2 (100) := USER;
TYPE t_rec IS RECORD
(
COLUMN_NAME VARCHAR2 (30 BYTE),
DATA_TYPE VARCHAR2 (106 BYTE),
DATA_LENGTH NUMBER
);
TYPE t_record IS TABLE OF t_rec;
-- initialization of record
v_record t_record := t_record ();
v_cntr NUMBER := 0;
v_status NUMBER;
sql_stmt VARCHAR2 (2000);
BEGIN
---dropping Already Existing temporary table
BEGIN
SELECT 1
INTO v_status
FROM all_objects
WHERE UPPER (object_name) = UPPER (tablname) AND owner = db_user;
--dbms_output.put_line( v_status );
IF v_status = 1
THEN
sql_stmt := 'drop table ' || tablname || '_new';
EXECUTE IMMEDIATE sql_stmt;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line ('Table not found--' || tablname);
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error while dropping table-' || SQLCODE || SQLERRM);
END;
---------------------------------------------------------------------------
---- retrieving the columns of the table
BEGIN
SELECT column_name, data_type, data_length
BULK COLLECT INTO v_record
FROM all_tab_columns
WHERE table_name = UPPER (tablname)
ORDER BY column_id;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error while retrieving the column details-' || SQLCODE || SQLERRM);
END;
FOR i IN 1 .. v_record.COUNT
LOOP
v_cntr := v_cntr + 1;
IF v_cntr = 1
THEN
IF v_record (i).data_type = 'DATE'
OR UPPER (v_record (i).data_type) = 'TIMESTAMP(6)'
THEN
sql_stmt :=
'create table '
|| ' '
|| tablname
|| '_new'
|| '('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| ')';
ELSE
sql_stmt :=
'create table '
|| ' '
|| tablname
|| '_new'
|| '('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| '('
|| v_record (i).data_length
|| '))';
END IF;
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt;
ELSE
IF v_record (i).data_type = 'DATE'
OR UPPER (v_record (i).data_type) = 'TIMESTAMP(6)'
THEN
sql_stmt :=
'alter table'
|| ' '
|| tablname
|| '_new'
|| ' '
|| 'add ('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| ')';
ELSE
sql_stmt :=
'alter table'
|| ' '
|| tablname
|| '_new'
|| ' '
|| 'add ('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| '('
|| v_record (i).data_length
|| '))';
END IF;
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt;
END IF;
END LOOP;
DBMS_OUTPUT.put_line ('Process Completed Successfully..!!');
END;
Output:
SQL> exec creat_tbl_frm_tbl('table2')
Process Completed Successfully..!!
PL/SQL procedure successfully completed.
SQL> select * from table2;
no rows selected
-- This is created with same columns as of passed table table2
SQL> select * from table2_new;
no rows selected
SELECT col1,col2,col3 --...
INTO newTable
FROM oldTable