Error using Oracle Ref Cursor - sql

I'm trying to do something fairly simple, I am trying to automate the removal and back up of tables from my personal table space. I have about 100 tables and want to get rid of all of them (except a table that I'm using to store the table names), but want to keep the data from the tables in case I need them sometime in the future. Below is the code I am trying to use to accomplish this. I am getting an error on the ref cursor, which I'll include below my code. I half expect somebody to tell me I'm an idiot and explain an easier way to do this. If not, please tell me what I'm doing wrong with the way that I am doing it, thanks.
DECLARE
v_folder_name VARCHAR2(100) := 'MY_FOLDER';
TYPE QRY_CURSOR IS REF CURSOR;
v_qry_cursor QRY_CURSOR;
v_file_name VARCHAR2(320);
v_file sys.utl_file.file_type;
v_max_buffer_length CONSTANT BINARY_INTEGER := 32767;
v_qry_str VARCHAR2(4000); --I've tried this with 32767, made no difference
v_drop_string VARCHAR2(4000);
v_dynamic_record VARCHAR2(4000); --tried this with 32767 also
CURSOR GET_TABLE_NAMES IS
SELECT * FROM TEMP_BACKUP_TABLE WHERE TABLE_NAME <> 'TEMP_BACKUP_TABLE';
FUNCTION startFile(file_name VARCHAR2)
--working function, used with many procedures, left out for brevity
END startFile;
FUNCTION closeFile(file_name VARCHAR2)
--working function, used with many procedures, left out for brevity
END closeFile;
BEGIN
INSERT INTO TEMP_BACKUP_TABLE SELECT DISTINCT TABLE_NAME FROM ALL_TAB_COLS WHERE OWNER = 'ME';
COMMIT;
FOR REC IN GET_TABLE_NAMES LOOP
v_file_name := REC.TABLE_NAME;
v_file := startFile(v_file_name);
v_qry_str := 'SELECT * FROM ' || v_file_name;
v_drop_string := 'DROP TABLE ' || v_file_name;
OPEN v_qry_cursor FOR v_qry_str; -- this is the line that returns an error
LOOP
FETCH v_qry_cursor INTO v_dynamic_record;
EXIT WHEN v_qry_cursor%NOTFOUND;
sys.utl_file.put_line(v_file, v_dynamic_record);
END LOOP;
CLOSE v_qry_cursor;
EXECUTE IMMEDIATE v_drop_string;
COMMIT;
v_file := closeFile(v_file_name);
END LOOP;
DELETE FROM TEMP_BACKUP_TABLE;
END;
The error I'm getting is as follows:
Error report:
ORA-00932: inconsistent datatypes: expected - got -
ORA-06512: at line 73
00932. 00000 - "inconsistent datatypes: expected %s got %s"
*cause:
*action:
Thanks for any help.

At a minimum, utl_file.put_line does not take arbitrary record and you can't fetch an arbitrary list of columns into a varchar2.
You could iterate over each column and construct a SQL statement that concatenates the values from each column into a single string. That would include doing things like putting a to_char with an explicit format mask on your date or timestamp columns, adding a delimiter, escaping any delimiters that exist in your data, etc. This is generally a rather tedious and error-prone process. And then you'll need to write a SQL*Loader control file to load the data back in the future.
It sounds like you'd be better off exporting the table using the Oracle export utility. That's a command-line utility (exp or expdp depending on whether you want to use the classic version or the DataPump version) that lets you export the table definition and data to a file that you can load later using the Oracle import utility.

Related

Oracle SQL - UNION breaks when "table does not exist"

I am new to Oracle SQL (and not generally experienced in SQL anyway). I would appreciate it if I could get some help with my problem.
I need to query several tables and views (30 or so) and do it as a UNION across all and return a single result set. However, some of the tables may or may not exist. I know the exact names of all 30 tables/views to be queried. I may do other queries prior to doing the large UNION, but eventually, I must return a single result of all rows. I cannot "define" variables in my sql script.
These are constraints placed on my and beyond my control. In order to work with the existing workflow, I have to operate within these contraints.
The problem is that if any of the tables/views do not exist, then the entire UNION fails and I get no results at all. I get this at the first occurrence of the missing view/table:
SQL Error: ORA-00942: table or view does not exist
00942. 00000 - "table or view does not exist"
*Cause:
*Action:
I would like be able to handle this without breaking. I'd like to simply skip over missing tables and return the remaining rows (though I know this is poor design to try to query without knowing if the table exists). It's ok if I end up including error messages as these will be skipped by the parser eventually.
I have something like this. If any of the views do not exist, everything fails and I get no results:
select col1 || col2 || ... from view1
union
select col1|| col2 || ... from view2
union
select name from view3
.
.
select ... from view30;
I can switch to PL/SQL if that helps. I don't have much experience with PL/SQL but I can use it if it can solve the problem. I did some reading on PL/SQL but could not figure out some straightforward way of doing this.
Any help is highly appreciated. Let me know if I can provide any other details.
Thanks.
Colleagues don't say that you CAN'T do that - they are saying you SHOULDN'T, cause of the fundamental problems. But the world isn't perfect - it often gives you a saw and tells you to hammer a nail. There is a solution to your problem - it involves dynamic SQL and some custom PL/SQL code. Simplified solution:
Create a package and a function:
function does_tbl_exists(p_tbl_name in varchar2) return number is
l_stmt varchar2(60);
begin
l_stmt := 'select count(*) from ' ||p_tbl_name;
execute immediate l_stmt;
return 1;
exception
when others then
return 0 ;
end;
Created a test table TEST_TBL with some data. And then we build up our SQL statement in a pl/sql block:
declare
l_count number;
l_stmt varchar2(400);
l_tab_name_1 varchar2(30) := 'TEST_TBL';
l_tab_name_2 varchar2(30) := 'TEST_TBL2';
begin
if test_pck.does_tbl_exists(p_tbl_name => l_tab_name_1) = 1 then
l_stmt := 'select count(*) count_number from ' || l_tab_name_1;
end if;
if test_pck.does_tbl_exists(p_tbl_name => l_tab_name_2) = 1 then
if l_stmt is not null then
l_stmt := l_stmt || ' union all' || chr(10);
end if;
l_stmt := l_stmt || 'select count(*) count_number from ' || l_tab_name_2;
end if;
if l_stmt is not null then
l_stmt := 'select sum(tmp.count_number) from (' || l_stmt || ') tmp';
dbms_output.put_line(l_stmt);
execute immediate l_stmt into l_count;
dbms_output.put_line('count: '||l_count);
else
dbms_output.put_line('Nothing todo.');
end if;
end;
By this template, you can build up your final SQL statement.
But use this as a last resort - I think you should still go to higher-ups and talk with them, that their current "requirement" is "bad" and they should feel "bad" (Insert meme here).

ORA-00900: invalid SQL statement error?What's wrong with my sql?

I try to write a function that split the string,but it shows:ORA-00900: invalid SQL statement error.What's wrong?I think that v_str varchar2(500); or v_strs_last varchar2(4000) := p_value; may be wrong.
CREATE OR REPLACE TYPE strsplit_type IS TABLE OF VARCHAR2 (4000);
create or replace function strsplit(p_value varchar2,
p_split varchar2 := ',')
return strsplit_type
pipelined is
v_idx integer;
v_str varchar2(500);
v_strs_last varchar2(4000) := p_value;
begin
loop
v_idx := instr(v_strs_last, p_split);
exit when v_idx = 0;
v_str := substr(v_strs_last, 1, v_idx - 1);
v_strs_last := substr(v_strs_last, v_idx + 1);
pipe row(v_str);
end loop;
pipe row(v_strs_last);
return;
end strsplit;
My oracle version is Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production.And I run the script in DB SOLO 5.
The error picture is:
Your DB Solo client seems to be interpreting the first semicolon it sees as the end of the statement, which is reasonable for plain SQL (DML or DDL) but not for PL/SQL.
You can see that from the log image you posted; it treats the create function ... v_ids integer part as one statement because that ends with the first semicolon - it compiles but with an error. Then it takes the next chunk, up to the next semicolon, as a separate statement - v_str varchar2(5000) - and it's that which gets the ORA-00900, since it is not valid SQL.
According to the documentation:
The statements must be separated by either semicolon or the 'GO' keyword. You can change the settings to only use a semicolon or 'GO' as statement separator instead of the default setting which accepts either one. When you have more than one statement in the Query Editor, DB Solo will send them to your server one at a time.
So based on that it doesn't seem to understand how to treat PL/SQL differently; but you can change your settings to not treat the semicolons as statement separators - across the board, which means you'd need to add GO after both the create type and create function statements, and any other queries or calls you make. This would be similar to using / everywhere in SQL*Plus or SQL Developer.
It may be easier to use the procedure editor. Presumably after you've created the type and function from the object browser.
Or, of course, use a different client...

SELECT from Collection denies to accept to_number/to_char

refering to my other post i decided to store numbers with leading zero in my database.
I'm selecting the imported data from a collection and insert it into a database table. The problem is that if I try to use the function to_char(c001,'009') on a column out of the collection it says "ORA-01722: invalid number".
I don't understand this because the datasource is varchar2 and the destination is varchar2, too.
Did anyone solve this issue?
Oracle's to_char function takes a number as it's first parameter. So the value you provide will be converted to number. If coo1 does not contain a valid number you will get that error message.
One of you numbers is not a number. You can use plsql code to find out what record contains an invalid number:
declare
l_number number;
cursor c is select ID,column_name from table;
begin
for r in c loop
begin
l_number := to_number(r.column_name);
exception
when others then dbms_output.put_line(r.id||' '||r.column_name);
end;
end loop;
end;

How do I fix auto increment in oracle after a data refresh

Every time my team does a data refresh to our UAT environment we have issues with the 'auto incremented' columns in oracle They hold onto the old value and therefore cause errors when a new insert happens. The only solution I have found is to use
select test_SEQ.nextval from test_table;
Until the next sequence is bigger then the max seq number in the table. I have over 200 tables to update, is there an easier why to do this?
Thanks
Erin
One better way to do this would be to drop the sequences and create new ones with the desired START WITH value. You could generate the DDL to do this dynamically.
Check the following sqlfiddle http://sqlfiddle.com/#!4/17345/1
It doesn't completely work due to limitations in sqlfiddle, but here's the function that makes it happen:
create or replace function
reset_sequence(p_sequence_name varchar,
p_table_name varchar,
p_column_name varchar)
return integer is
v_temp integer;
v_sql varchar(2000);
begin
v_sql := 'select nvl(max('||p_column_name||'),0)+1 col_name from '||p_table_name;
execute immediate v_sql INTO v_temp;
v_sql := 'drop sequence '||p_sequence_name;
execute immediate v_sql;
v_sql := 'create sequence '||p_sequence_name||' start with '||v_temp;
execute immediate v_sql;
return v_temp;
end;
Basically you call this function and pass a schema name, table name and column name and it will set the function to the correct value. You can put a begin/exception/end block to ignore errors when dropping the sequence, in case it doesn't exist, but all that is just icing. You could also have it detect the column that is the primary key if you wanted, but no real way in Oracle to detect the sequence name.
You could also make a procedure version of this, but I tend to prefer functions for whatever reason.

dynamic declaration/query in oracle 9i

In Oracle, given a list of table names, I want to perform 'select column1 into var1 from table' statements on a mass number of tables. And I want to do this for all columns of a table. I cannot declare the type of var1 until the query with user_tab_columns returns the column's type. I tried to declare var1 as sys.anytype but got ORA-00932 with error message such as "inconsistent datatypes: expected CHAR got CHAR".
So how can I get past this error or how can I dynamically declare a variable? Many thanks.
Most datatypes will implicitly convert into a VARCHAR. Obviously there are exceptions, but if your tables are just varchars, dates, and numbers then you should be fine.
Craig is right you should probably declare it as a VARCHAR2 instad of an anytype.
This article by Jeff Hunter has a nice function that makes this easy to populate your variable in way that won't break if your data can't be converted.
CREATE OR REPLACE FUNCTION getData(data IN SYS.ANYDATA)
RETURN VARCHAR2
AS
l_varchar2 VARCHAR2(4000);
l_rc NUMBER;
BEGIN
CASE data.getTypeName
when 'SYS.NUMBER' then
l_rc := data.getNumber(l_varchar2);
when 'SYS.DATE' then
l_rc := data.getDate(l_varchar2);
when 'SYS.VARCHAR2' then
l_rc := data.getVarchar2(l_varchar2);
else
l_varchar2 := '** unknown **';
END CASE;
RETURN l_varchar2;
END;