dynamic declaration/query in oracle 9i - sql

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;

Related

Error "Encountered the symbol "IN" when expecting one of the following" in Oracle

This is the code:
CREATE PROCEDURE print_string(IN input_string VARCHAR(255))
BEGIN
DECLARE num_chars INT DEFAULT 0;
IF input_string IS NULL THEN
SET num_chars = 0;
ELSE
SET num_chars = CHAR_LENGTH(input_string);
END IF;
SELECT UPPER(input_string), num_chars;
END;
I get error:
PLS-00103: Encountered the symbol "IN" when expecting one of the following: <an identifier> <a double-quoted delimited-identifier>
current delete exists prior
Errors: check compiler log
How do I fix: current delete exists prior?
The immediate error is that you have the argument name and mode the wrong way around - it should be (input_string IN ... not (IN input_string .... But there are other problems:
Oracle recommends VARCHAR2 over VARCHAR.
arguments just have the data type, not a size (or precision/scale), so it should be (input_string IN VARCHAR2) not (input_string IN VARCHAR2(255).
you are missing the IS/AS keyword.
DECLARE comes before BEGIN in a PL/SQL block; having a nested block here would be valid, but you're missing a BEGIN and END; if you do that, and it isn't necessary so I don't think it's what you meant. And you don't need the DECLARE at all for a procedure, it's implied.
if you want a default value for a PL/SQL variable then assign it, rather than using DEFAULT. (You don't really need to do this here, as you always assign a value later anyway, but I'm sticking with your general approach.)
it's probably better to use native Oracle types, so NUMBER or PLS_INTEGER instead of INT.
assignment of values is with :=, not SET ... = ....
CHAR_LENGTH should just be LENGTH (unless you have your own function with that name).
in PL/SQL you have to select into something, and from something. But if you do that here, you still have to return it to the caller somehow.
given that you want to 'print' the string, you probably want dbms_output - though that relies on the client showing the result, which most don't by default, and it's generally only used for debugging...
So this would work:
CREATE PROCEDURE print_string(input_string IN VARCHAR2) AS
num_chars PLS_INTEGER := 0;
BEGIN
IF input_string IS NULL THEN
num_chars := 0;
ELSE
num_chars := LENGTH(input_string);
END IF;
DBMS_OUTPUT.PUT_LINE(UPPER(input_string) || ': ' || num_chars);
END;
/
BEGIN
DBMS_OUTPUT.ENABLE;
print_string('This is a test');
END;
/
1 rows affected
dbms_output:
THIS IS A TEST: 14
fiddle
But again, dbms_output isn't ideal. And it could be done much more simply (#Mto has shown one way), or without using PL/SQL at all.
You can fix the issues (listing in #Alex Poole's answer) and simplify the procedure to:
CREATE PROCEDURE print_string(
input_string IN VARCHAR2
)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(UPPER(input_string) || ', ' || COALESCE(LENGTH(input_string), 0));
END;
/
Then:
BEGIN
DBMS_OUTPUT.ENABLE;
print_string('This is a test');
print_string(NULL);
END;
/
Outputs:
THIS IS A TEST, 14
, 0
fiddle
The code syntax is incorrect here. It should be something like
CREATE OR REPLACE PROCEDURE print_string(input_string IN VARCHAR2)
IS
BEGIN

How to make a function that takes code like "SELECT * FROM SOME_TABLE" as an input and returns a table as an output?

I want to create a function that takes some code as an input (e.g. Select * FROM SOME_TABLE) and returns the result of a query as an output.
I want to use it in procedures in order to return tables as a result.
It should look like this:
BEGIN
--some procedure code
CREATE TABLE SOME_TABLE as Select * FROM ...;
Select * FROM table(my_function('Select * FROM SOME_TABLE'));
END;
Important tips:
The resulting table can have multiple columns, from 1 to +inft
The resulting table can have multiple rows, from 1 to +inft
So the size of a table can be both very small or very large.
The input query can have several where, having, partition, and other Oracle constructions.
I want to have a table as an output, not DBMS_OUTPUT.
I can't install any modules/applications, or use other languages hints. However, I can manually create types, functions, procedures.
I tried to search in the net but could not find a solution that meets all my expectations. The best link I've found was this:
https://sqljana.wordpress.com/2017/01/22/oracle-return-select-statement-results-like-sql-server-sps-using-pipelined-functions/
DBMS_SQL.RETURN_RESULT works if your "code" is a select query
DECLARE
l_cur SYS_REFCURSOR;
l_query VARCHAR2(4000) := 'select * from SOME_TABLE';
BEGIN
OPEN l_cur for l_query;
DBMS_SQL.RETURN_RESULT(l_cur);
END;
/
you can create a function that has a string as parameter and return a cursor.
select statement you should pass as a string. in a function you can open a Cursor.
declare
v_sql varchar2(100) := 'select 1,2,3,4,5 from dual';
cur_ref SYS_REFCURSOR;
function get_data(in_sql in varchar2) return SYS_REFCURSOR
as
cur_ret SYS_REFCURSOR;
begin
OPEN cur_ret FOR in_sql;
return cur_ret;
end;
begin
:cur_ref := get_data(v_sql);
end ;
if your select statement is longer than 32K than you maybe should use a clob instead of varchar2 for your Parameter type. But you have to try that

Ways of handling unknown data type in oracle

I have a statement that executes a sql like this:
execute immediate cursor_rule.rule_sql into rule_result ;
my problem is that the output of rule_sql can be anything from null, to boolean to a number.
How do I define rule_result in a situation like this?
You can use:
DECLARE
rule_result VARCHAR2(4000);
BEGIN
EXECUTE IMMEDIATE :your_sql INTO rule_result;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL; -- Handle what should happen when the SQL returns zero rows.
WHEN TOO_MANY_ROWS THEN
NULL; -- Handle what should happen when the SQL returns two or more rows.
END;
/
If the result of your sql statement is a:
String data type then it gets stored in the rule_result as is.
numeric data type then Oracle will implicitly call TO_CHAR on it to convert it to a VARCHAR2 value exactly long enough to hold its significant digits.
DATE data type then Oracle will implicitly call TO_CHAR( date_value, NLS_DATE_FORMAT ) using the NLS_DATE_FORMAT session parameter as the format model to convert it to a string.
TIMESTAMP data type then Oracle will implicitly call TO_CHAR( timestamp_value, NLS_TIMESTAMP_FORMAT ) using the NLS_TIMESTAMP_FORMAT session parameter as the format model to convert it to a string.
You can parse the SQL statement using DBMS_SQL to discover the column data type. For example:
declare
l_cursor_id pls_integer := dbms_sql.open_cursor;
l_pointless_count pls_integer;
l_desc_cols dbms_sql.desc_tab;
l_sql long := 'select dummy as teststring, 123 as testnum, sysdate as testdate from dual';
begin
dbms_sql.parse(l_cursor_id, l_sql, dbms_sql.native);
dbms_sql.describe_columns(l_cursor_id, l_pointless_count, l_desc_cols);
for i in 1..l_desc_cols.count loop
dbms_output.put_line
( rpad(l_desc_cols(i).col_name,31) || lpad(l_desc_cols(i).col_type,4) );
end loop;
dbms_sql.close_cursor(l_cursor_id);
end;
Output:
TESTSTRING 1
TESTNUM 2
TESTDATE 12
Type codes are defined in DBMS_TYPES and the documentation (which as I discovered last week do not necessarily agree).

Error using Oracle Ref Cursor

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.

Concatenate CLOB-rows with PL/SQL

I've got a table which has an id and a clob content like:
Create Table v_example_l (
nip number,
xmlcontent clob
);
We insert our data:
Insert into V_EXAMPLE_L (NIP,XMLCONTENT)
Values (17852,'<section><block><name>delta</name><content>548484646846484</content></block></section>');
Insert into V_EXAMPLE_L (NIP,XMLCONTENT)
Values (17852,'<section><block><name>omega</name><content>545648468484</content></block></section>');
Insert into V_EXAMPLE_L (NIP,XMLCONTENT)
Values (17852,'<section><block><name>gamma</name><content>54564846qsdqsdqsdqsd8484</content></block></section>');
I'm trying to do a function that concatenates the rows of the clob that gone be the result of a select, i mean without having to give multiple parameter about the name of table or such, i should only give here the column that contain the clobs, and it should handle the rest.
CREATE OR REPLACE function assemble_clob(q varchar2)
return clob
is
v_clob clob;
tmp_lob clob;
hold VARCHAR2(4000);
--cursor c2 is select xmlcontent from V_EXAMPLE_L where id=17852
cur sys_refcursor;
begin
OPEN cur FOR q;
LOOP
FETCH cur INTO tmp_lob;
EXIT WHEN cur%NOTFOUND;
--v_clob := v_clob || XMLTYPE.getClobVal(tmp_lob.xmlcontent);
v_clob := v_clob || tmp_lob;
END LOOP;
return (v_clob);
--return (dbms_xmlquery.getXml( dbms_xmlquery.set_context("Select 1 from dual")) )
end assemble_clob;
The function is broken... (if anybody could give me a help, thanks a lot, and i'm noob in sql so ....). Thanks!
You don't really say why it's broken but the DBMS_LOB package has an APPEND function that might be what you're looking for.
You'll have to explain what is happening that is not what you expect. Your example works fine for me. In SQLPlus, note the need to SET LONG to a large enough value to fetch the entire CLOB contents.
dev> set long 2000
dev> select assemble_clob('select xmlcontent from v_example_l') from dual;
ASSEMBLE_CLOB('SELECTXMLCONTENTFROMV_EXAMPLE_L')
--------------------------------------------------------------------------------
<section><block><name>delta</name><content>548484646846484</content></block></se
ction><section><block><name>omega</name><content>545648468484</content></block><
/section><section><block><name>gamma</name><content>54564846qsdqsdqsdqsd8484</co
ntent></block></section>
try using
DBMS_LOB.append (v_clob,tmp_lob);