Cursor and table cannot be found - sql

I have a procedure that will select MAX from some tables, but for some reason it is not able to find these tables. Could anybody help me?
declare
varible1 varchar2 (255);
temp varchar2 (255);
last_val number(9,0);
cursor c1 is Select distinct table_name from user_tab_cols order by table_name;
begin
FOR asd in c1
LOOP
temp := asd.table_name;
varible1 := '"'||temp||'"';
select max("id") into last_val from varible1 ;
END LOOP;
end;
For example, the first table name is acceptance_form and for select I need to use "acceptance_form".
Code after edit:
declare
varible1 varchar2 (255);
temp varchar2 (255);
last_val number(9,0);
cursor c1 is Select distinct table_name from user_tab_cols where column_name = 'id';
begin
FOR asd in c1
LOOP
temp := asd.table_name;
execute immediate 'select NVL(max('||'id'||'),0) from "'||varible1||'"' into last_val;
END LOOP;
end;
Can't cuz db is Case sensitive Oracle express 10g tables and rows was created like this
CREATE TABLE "ADMINMME"."acceptance_form"
(
"group_id" NUMBER(9, 0),
"id" NUMBER(4, 0) DEFAULT '0' NOT NULL ,
"is_deleted" NUMBER(4, 0),
"name" NVARCHAR2(30) NOT NULL
);
Can u tell me how to handle exception sequence dosn't exist for this;
Nevermind exception was in wrong block :)
declare
temp varchar2 (255);
last_val number(9,0);
cursor c1 is Select distinct table_name from user_tab_cols where column_name = 'id';
begin
FOR asd in c1
LOOP
temp := asd.table_name;
execute immediate 'select NVL(max("id"),0)+1 from "'||temp||'"' into last_val;
begin
EXECUTE IMMEDIATE 'drop sequence "seq_'|| temp||'"';
EXECUTE IMMEDIATE 'create SEQUENCE "seq_'|| temp ||'" MINVALUE '||last_val||'MAXVALUE 999999999999999999999999999 INCREMENT BY 1 NOCACHE';
EXECUTE IMMEDIATE 'select '||temp||'.nextval from dual';
EXECUTE IMMEDIATE 'ALTER SEQUENCE "seq_'||temp||'" INCREMENT BY 1';
exception when others then
null;
end;
END LOOP;
end;

Dynamic sql doesn't work in that way.
declare
varible1 varchar2 (255);
temp varchar2 (255);
last_val number(9,0);
cursor c1 is Select distinct table_name from user_tab_cols order by table_name;
begin
FOR asd in c1
LOOP
temp := asd.table_name;
begin
execute immediate 'select max(id) from '||temp into last_val;
dbms_output.put_line('max(id) for table: ' ||temp||' = '||last_val);
exception when others then
dbms_output.put_line('Failed to get max(id) for table: ' ||temp);
end;
END LOOP;
end;

You can't use a variable for the table name.
What you can do is creating the complete sql statement as a string and use execute immediate
Here are some examples how to do that: http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/dynamic.htm#CHDGJEGD

Related

ORACLE SQL CURSOR / FOR LOOP

I need to verify converted data, distinct values and records counts. I would like to write statements so that I can enter a table name, then retrieve it's columns and use them in a query to get its distinct values (the actual values, not just a count of how many distinct) and their count.
I think I need to a CURSOR or CURSOR FOR LOOP and create something like this:
declare
cursor field_name
is
select COLUMN_NAME
from user_tab_cols
where table_name='TABLE1'
c_field_name field_name%ROWTYPE;
BEGIN
OPEN field_name
loop
fetch field_name INTO c_field_name;
exit when field_name%NOTFOUND;
end loop;
CLOSE field_name;
end;
Then run a query using that above in something like
select field_name, count(*)
from table1
group by field_name
Do I need to create 2 loop statements? I've not yet created one and can't quite get the context to get my results so far.
BEGIN
FOR myrow in (select field_name, count(*) as "count" from table1 group by field_name)
loop
dbms_output.put_line(myrow.field_name);
dbms_output.put_line(myrow.count);
end loop;
end;
Considering you will be giving the table name as parameter below code will print all the values of all the columns one by one along with the count of the values
create or replace PROCEDURE PR_PREP(
P_TABLE_NAME IN VARCHAR2)
IS
CURSOR CUR_COLUMNS (PA_TABLE_NAME VARCHAR2)
IS
SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = PA_TABLE_NAME;
COL_NAMES CUR_COLUMNS%ROWTYPE;
TYPE TYP_RECORD
IS
RECORD
(
FIELD_NAME VARCHAR2(255),
CNT INT);
TYPE TYP_OP_TABLE
IS
TABLE OF TYP_RECORD;
OP_TABLE TYP_OP_TABLE;
I INT;
V_SQL VARCHAR2(2000);
BEGIN
FOR COL_NAMES IN CUR_COLUMNS(P_TABLE_NAME)
LOOP
V_SQL := 'SELECT ' || COL_NAMES.COLUMN_NAME || ' AS FIELD_NAME ,
COUNT(*) AS CNT FROM ' ||
P_TABLE_NAME || ' GROUP BY ' || COL_NAMES.COLUMN_NAME ;
-- DBMS_OUTPUT.PUT_LINE (V_SQL);
EXECUTE IMMEDIATE V_SQL BULK COLLECT INTO OP_TABLE;
dbms_output.put_line('columna name = ' ||COL_NAMES.COLUMN_NAME);
FOR I IN OP_TABLE.FIRST .. OP_TABLE.LAST
LOOP
DBMS_OUTPUT.PUT_LINE('FIELD VALUE '||OP_TABLE(I).FIELD_NAME || ' COUNT = ' || OP_TABLE(I).CNT);
END LOOP;
DBMS_OUTPUT.PUT_LINE('ONE FILED ENDED , NEXT STARTED');
END LOOP;
END;

Iterate through row's columns

I have table with 100 columns with not correlated names (ABC1, DA23, EE123 - there is no common pattern there).
I want to iterate through every row and every column in this table.
My current script:
BEGIN
FOR single_row IN (
SELECT *
FROM MY_TABLE)
LOOP
--iterate through columns of 'single_row'
--for each nullable column do insert with real current column name and column value)
--I assume each column is nullable except of ID
INSERT INTO ANOTHER_TABLE VALUES (single_row.id, column_name, column_value);
END LOOP;
END;
So for example, if MY_TABLE contains 2 rows:
ID|ABC1|DA23|EE123|...
1|123|456|789|...
2|321|654|987|...
After running my script, my ANOTHER_TABLE will contain:
MY_TABLE_ID|COLUMN_NAME|COLUMN_VALUE
1|ABC1|123
1|DA23|456
1|EE123|789
... other columns from row 1
2|ABC1|321
2|DA23|654
2|EE123|987
... other columns from row 2
How I can do this?
I'm using Oracle 11g
EDIT
#vkp provided great solution, but there is one more thing to solve. I don't want to specify all columns in in clause. I would love to use some kind of query there or * or anything else, just to not be forced to list all of them.
I have tried something like this:
select *
from MY_TABLE t
unpivot (
column_value for column_name in (select column_name
from user_tab_columns
where table_name = 'MY_TABLE'
and nullable = 'Y')
) u
but it returns error:
ORA-00904: : invalid identifier
00904. 00000 - "%s: invalid identifier"
This is an application of unpivot.
select *
from my_table m
unpivot (column_value for column_name in (ABC1,DA23,EE123)) u
null values for any of the columns for an id won't be shown in the result.
If you have to include null values in the output, use the option INCLUDE NULLS.
select *
from my_table m
unpivot include nulls (column_value for column_name in (ABC1,DA23,EE123)) u
Edit: To include column names dynamically, use
DECLARE
sql_stmt VARCHAR2(4000);
var_columns VARCHAR2(4000); --use clob datatype if the column names can't fit in with this datatype
BEGIN
SELECT LISTAGG(column_name,',') WITHIN GROUP(ORDER BY column_name)
INTO var_columns
FROM user_tab_columns
WHERE table_name='MY_TABLE' AND column_name<>'ID';
sql_stmt:='select * from my_table m
unpivot
(column_value for column_name in (' || var_columns || ')) u';
EXECUTE IMMEDIATE sql_stmt;
END;
/
First option. With dynamic sql.
declare
v_ctx number;
v_query varchar2(500);
v_total NUMBER;
v_desctab DBMS_SQL.DESC_TAB;
v_column_cnt NUMBER;
v_value varchar2(32767);
v_result clob := '';
v_rownum number := 0;
begin
v_ctx := dbms_sql.open_cursor;
v_query := 'select * from user_objects where rownum < 100';
dbms_sql.parse(v_ctx,v_query,dbms_sql.v7);
v_total := dbms_sql.execute(v_ctx);
DBMS_SQL.DESCRIBE_COLUMNS(v_ctx, v_column_cnt, v_desctab);
for i in 1 .. v_column_cnt loop
dbms_sql.define_column(v_ctx, i, v_value /* data_type varchar2*/, 32767 /* max_length*/);
end loop;
loop
exit when dbms_sql.fetch_rows(v_ctx) = 0;
v_rownum := v_rownum +1;
for i in 1 .. v_column_cnt loop
dbms_sql.column_value(v_ctx, i, v_value);
dbms_output.put_line(v_rownum||' - '||v_desctab(i).col_name||' - '||v_value);
end loop;
end loop;
dbms_sql.close_cursor(v_ctx);
exception
when others then
dbms_sql.close_cursor(v_ctx);
raise;
end;
/
2nd option with xquery.
select t1.id,t2.* from xmltable('for $i in ora:view("<you_table_here>")/ROW
return $i'
columns id FOR ORDINALITY
, row_value xmltype path'.'
) t1
,xmltable('for $i in $row_value/ROW/* return $i'
passing t1.row_value as "row_value"
columns col_index for ORDINALITY ,
column_name varchar2(100) path 'name()',
column_value varchar2(100) path 'text()'
) t2
Here is a simple solution using REF CURSOR.
I've tried this code and it's working at my end.
DECLARE
query_2 VARCHAR2(1000);
TYPE icur IS REF CURSOR;
ic icur;
col_val VARCHAR2(100);
BEGIN
FOR j IN
(SELECT * FROM user_tab_cols WHERE table_name = UPPER('MY_TABLE'))
LOOP
dbms_output.put_line(j.column_name);
query_2 := 'SELECT ' || j.column_name|| ' FROM MY_TABLE';
OPEN ic FOR query_2;
LOOP
FETCH ic INTO col_val;
EXIT WHEN ic%NOTFOUND;
INSERT INTO ANOTHER_TABLE VALUES( j.column_name, col_val);
END LOOP;
END LOOP;
END;
/

How to declare %rowtype dynamically?

Below is the sample code in which I have stored all the table names in one table (table_config) and trying to insert one record of every table into its temporary table and trying to get the particular rowid for further need.
So I need every table rowtype to make this work, something dynamic. Could you please help me with this?
DECLARE
l_row table_name%ROWTYPE;
l_rowid ROWID;
l_table_name all_tab_partitions.table_name%TYPE;
l_temp_table_name all_tab_partitions.table_name%TYPE;
BEGIN
FOR tab IN
(select table_name from
Table_config)
LOOP
l_table_name:= tab.table_name;
l_temp_table_name:= 'TEMP_'||l_table_name;
SELECT * INTO l_row
FROM l_table_name
WHERE ROWNUM=1;
INSERT INTO l_temp_table_name VALUES l_row
RETURNING ROWID INTO l_rowid;
COMMIT;
END LOOP;
END;
Thank you,
Pradeep
Without coding the complete answer for you.
Why don't you do something like this?
FOR tab IN
(select table_name from
Table_config)
EXECUTE_IMMEDIATE(
'declare
l_row '||table_name||'%ROWTYPE;
begin
INSERT INTO '||l_temp_table_name
SELECT * FROM '||l_table_name||' WHERE ROWNUM=1;
end;');
EXECUTE_IMMEDIATE ('SELECT ROWID FROM '||l_table_name)
INTO l_rowid;
END LOOP;
it assumes target table is empty to begin with with only one record inserted during the process.
You can't do that as already mentioned in the comment by OldProgrammer above.
You'll have to use Dynamic SQL to achieve what you're trying to achieve.
DECLARE
temp_table VARCHAR2(255);
source_table VARCHAR2(255);
sql_stmt VARCHAR2(255);
CURSOR c1 IS
SELECT table_name FROM user_Tables;
BEGIN
FOR c1_Rec IN c1 LOOP
temp_table := 'TEMP_'||c1_rec.table_name;
source_table := c1_rec.table_name;
sql_stmt := 'INSERT INTO '||temp_table||' SELECT * FROM '||source_table||' WHERE rownum = 1';
EXECUTE IMMEDIATE sql_stmt;
END LOOP;
END;
/
Below is solution. What do you need this rowids for? I would be much simpler without it, as you cannot use returning with insert as select
DECLARE
l_rowid ROWID;
l_table_name all_tab_partitions.table_name%TYPE;
l_temp_table_name all_tab_partitions.table_name%TYPE;
v_sql1 varchar2(4000);
v_sql2 varchar2(4000);
BEGIN
FOR tab IN (select table_name from Table_config) LOOP
l_table_name:= tab.table_name;
l_temp_table_name:= 'TEMP_'||l_table_name;
v_sql1 := 'select rowid from ' || l_table_name || ' where rownum =1 for update';
v_sql2 := 'insert into ' || l_temp_table_name || ' select * from ' || l_table_name || ' where rownum = 1';
execute immediate v_sql1 into l_rowid;
execute immediate v_sql2;
commit;
END LOOP;
END;
/
You should investigate EXECUTE IMMEDIATE INTO. I think this would be an excellent way to get the ROWID when combined with some dynamic SQL examples from above. Here's an example:
DECLARE
DYN_SQL VARCHAR(4000) := 'SELECT 1 FROM DUAL';
INTO_VAR NUMBER(1);
BEGIN
EXECUTE IMMEDIATE DYN_SQL INTO INTO_VAR;
DBMS_OUTPUT.PUT_LINE(INTO_VAR);
END;
Thank you guys for your response. Actually I was trying to implement partition exchange on interval partitioned tables. I achieved it by using Dynamic Sql now. Initially I was trying to implement it by using rowid which is ok when I hard coded for one table, but when I thought of configuring it and using it for multiple tables I got stuck at that %ROWTYPE.
In the below code I have hard coded table name in few places which can be modified as dynamic but the problem is how to get the %ROWTYPE for the every table we pass.
DECLARE
l_table_name table_config.table_name%TYPE;
l_query_temp VARCHAR2(1000);
l_part_table_name all_tab_partitions.table_name%TYPE;
l_part_name all_tab_partitions.partition_name%TYPE;
l_temp_table_name all_tab_partitions.table_name%TYPE;
l_row test_archival%ROWTYPE;
l_rowid ROWID;
l_arch_table_name all_tab_partitions.table_name%TYPE;
l_arch_part_name VARCHAR2(30);
l_query_arch VARCHAR2(1000);
l_query_source VARCHAR2(1000);
BEGIN
<<outer_loop>>
FOR tab IN
(SELECT table_name FROM
table_config)
LOOP
l_table_name:= tab.table_name;
<<inner_loop>>
FOR part IN
(SELECT table_name, partition_position, partition_name FROM
(SELECT table_name, partition_position, partition_name,
DENSE_RANK() OVER (PARTITION BY table_name ORDER BY partition_position DESC) AS RANK
FROM all_tab_partitions
WHERE table_name=l_table_name
) WHERE RANK NOT IN(1, 2) ORDER BY partition_position)
LOOP
l_part_table_name:= part.table_name;
l_part_name:= part.partition_name;
l_temp_table_name := 'TEMP_'||l_part_table_name;
l_arch_table_name := 'ARCH_'||l_part_table_name;
l_query_temp := 'ALTER TABLE '
|| l_part_table_name
|| ' EXCHANGE PARTITION '
|| l_part_name
|| ' WITH TABLE '
|| l_temp_table_name
||' INCLUDING INDEXES WITHOUT VALIDATION';
EXECUTE IMMEDIATE l_query_temp;
COMMIT;
SELECT * INTO l_row FROM temp_test_archival WHERE ROWNUM = 1;
INSERT INTO arch_test_archival VALUES l_row RETURNING ROWID INTO l_rowid;
COMMIT;
SELECT subobject_name
INTO l_arch_part_name FROM user_objects
WHERE data_object_id = dbms_rowid.rowid_object(l_rowid);
DELETE from arch_test_archival where rowid=l_rowid;
COMMIT;
l_query_arch := 'ALTER TABLE '
||'ARCH_TEST_ARCHIVAL'
||' EXCHANGE PARTITION '
||l_arch_part_name
||' WITH TABLE '
||'TEMP_TEST_ARCHIVAL'
||' INCLUDING INDEXES WITHOUT VALIDATION';
EXECUTE IMMEDIATE l_query_arch;
END LOOP;
END LOOP;
END;
/

PL/SQL if table not exist create

Hello i use oracle SQL developer
I have create a procedure, and i need to check if a table exist, if not exist i must create how can do?
I have try this
DECLARE v_emp int:=0;
BEGIN
SELECT count(*) into v_emp FROM dba_tables;
if v_emp = 0 then
EXECUTE IMMEDIATE 'create table EMPLOYEE ( ID NUMBER(3), NAME VARCHAR2(30) NOT NULL)';
end if;
END;
but give me an error 00103 because not find table
Just execute the create and watch the exception if thrown. Oracle would never replace the DDL of a table.
declare
error_code NUMBER;
begin
EXECUTE IMMEDIATE 'CREATE TABLE EMPLOYEE(AGE INT)';
exception
when others then
error_code := SQLCODE;
if(error_code = -955)
then
dbms_output.put_line('Table exists already!');
else
dbms_output.put_line('Unknown error : '||SQLERRM);
end if;
end;
You can run this for example:
if (select count(*) from all_tables where table_name = 'yourTable')>0 then
-- table exists
else
-- table doesn't exist
end if;
You should try following,
declare
nCount NUMBER;
v_sql LONG;
begin
SELECT count(*) into nCount FROM dba_tables where table_name = 'EMPLOYEE';
IF(nCount <= 0)
THEN
v_sql:='
create table EMPLOYEE
(
ID NUMBER(3),
NAME VARCHAR2(30) NOT NULL
)';
execute immediate v_sql;
END IF;
end;

procedure to delete duplicate records using dynamic sql

i have successfully implemented a proc which can delete delete duplicate records from a table .
Carrying on i am further trying to generalize this procedure using Dynamic sql But i get the following error when compiling itself .
ERROR line 13, col 7, ending_line 14, ending_col 14, Found ''SELECT ROWID, ERR_MOD
FROM '', Expecting: ( SELECT -or- WITH
Here is my code :-
CREATE OR REPLACE PROCEDURE BIOCON.proc_dedup (
p_tblname IN VARCHAR2,
p_cname IN varchar2,
p_cvalue IN varchar2,
p_out OUT VARCHAR2:=0
)
IS
v_cnt NUMBER;
CURSOR TMP
IS
'SELECT ROWID, ERR_MOD
FROM ' || p_tblname||
'WHERE '|| p_cname '='||''p_cvalue '';
BEGIN
BEGIN
'SELECT COUNT ( * )
INTO v_cnt
FROM ' || p_tblname||
'WHERE '|| p_cname' = '||''p_cvalue'';
EXCEPTION
WHEN OTHERS
THEN
p_out:=1;
end;
FOR REC IN tmp
LOOP
IF v_cnt = 1
THEN
EXIT;
ELSE
'DELETE FROM '|| p_tblname||
'WHERE ROWID = REC.ROWID';
END IF;
v_cnt := v_cnt - 1;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
p_out := 2;
END;
/
Hi you cannot create a dynamic query for normal cursors in such cases you should use ref cursors and follow the the steps below
--create a query string
--open the ref cursor
and please use execute immediate for execution of delete statements...
CREATE OR REPLACE PROCEDURE BIOCON.proc_dedup (
p_tblname IN VARCHAR2,
p_cname IN varchar2,
p_cvalue IN varchar2,
p_out OUT VARCHAR2:=0
IS
v_cnt NUMBER;
v_string varchar2(200);
TYPE ref_cur IS REF CURSOR
ref_cursor_name ref_cur;
BEGIN
v_string:='SELECT ROWID, ERR_MOD FROM ' || p_tblname|| 'WHERE '|| p_cname
'='||''p_cvalue '';
OPEN cursor_variable_name FOR v_string;
.....
.....
EXECUTE IMMEDIATE 'DELETE FROM '|| p_tblname|| 'WHERE ROWID = REC.ROWID';--you need execute immediate for this
.....
.....