ORA-00900: invalid SQL statement error?What's wrong with my sql? - 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...

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

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.

Oracle SQL Developer, using dynamic SQL in function

I have the following script which contains a function named 'myFunction'. (declaration of types named rowValueTmp and rowValueTable are also attached for your information) Basically, I need to use a table name as an input parameter for myFunction. I found that I need to use dynamic SQL in order to use the table name as a parameter (Please correct me if there are alternative ways to do this). So the following code is what I have tried so far.
create or replace type rowValueTmp as object (
month number,
year number
);
/
create or replace type rowValueTable as table of rowValueTmp;
/
create or replace FUNCTION myFunction (TABLENAME in VARCHAR2)
return rowValueTable as
v_ret rowValueTable;
begin
execute immediate '
select rowValueTmp(month, year)
bulk collect into v_ret
from '||TABLENAME;
return v_ret;
end myFunction;
/
select * from table(myFunction('SCHEMA.TEST'));
But, this code gives me an error, and I assumed that this error is occurred because of using 'bulk collect' in execute immediate block.
ORA-03001: unimplemented feature
If I replace the content of execute immediate as the following, the above script is working..
select rowValueTmp(month, year)
bulk collect into v_ret
from SCHEMA.TEST;
Question
1] Is there any way(rather than Dynamic SQL) that I can use a table name as an input parameter for myFunction?
2] If I am not allowed to use bulk collect in execute immediate block, what do you suggest?
You can return values from execute immediately into a bulk collect:
CREATE OR REPLACE FUNCTION myfunction (tablename IN VARCHAR2)
RETURN rowvaluetable AS
v_ret rowvaluetable;
v_table VARCHAR2 (61) := DBMS_ASSERT.sql_object_name (tablename);
BEGIN
EXECUTE IMMEDIATE '
select rowValueTmp(month, year)
from ' || v_table
BULK COLLECT INTO v_ret;
RETURN v_ret;
END myfunction;
/
In the interest of an abundance of caution, I'd recommend using DBMS_ASSERT to validate the table parameter as well (as shown).

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.

Procedures or Functions (Pl/sql)

I am currently learning pl/sql using oracle 10g
I have a certain confusion
When should I use stored procedures and when should i go for functions?
Please help me out with some real world example.
Thank you.
A function returns a value, although that could actually be an object like a cursor.
Also only a function can be defined with the following (as of 11.1):
DETERMINISTIC option, which helps the optimizer avoid redundant function calls.
PARALLEL_ENABLED option, which allows the function to be used safely in slave sessions of parallel DML evaluations.
PIPELINED option, which returns the results of a table function iteratively.
RESULT_CACHE option, which stores function results in the PL/SQL function result cache.
RESULT_CACHE clause, which specifies the data sources on which the results of a function.
So if you need to return multiple values, use a procedure. However be aware that the above five features are then not available.
If you want to include a PL/SQL subprogram in a SQL statement then you probably want a function.
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/subprograms.htm#CHDBEJGF
DECLARE
l_user_id VARCHAR2(1);
l_received_user VARCHAR2(30);
PROCEDURE print_user_name(user_name_in IN VARCHAR2)
AS
BEGIN
DBMS_OUTPUT.PUT_LINE('The user''s name is: ' || INITCAP(user_name_in));
END print_user_name;
FUNCTION get_user_name(user_id_in IN VARCHAR2) RETURN VARCHAR2
AS
l_user_name VARCHAR2(30);
BEGIN
SELECT 'Amanda'
INTO l_user_name
FROM dual
WHERE dummy = user_id_in;
RETURN l_user_name;
END get_user_name;
BEGIN
-- excute an action --
print_user_name('John');
l_user_id := 'X';
-- hold action's result in a variable --
l_received_user := get_user_name(l_user_id);
-- work with the received result/variable --
DBMS_OUTPUT.PUT_LINE('The received user''s name is: ' || INITCAP(l_received_user));
IF l_received_user = 'John' THEN
DBMS_OUTPUT.PUT_LINE('The received user''s name is John');
ELSE
DBMS_OUTPUT.PUT_LINE('The received user''s name is not John');
END IF;
END;
/*
The user's name is: John
The received user's name is: Amanda
The received user's name is not John
*/
Difference is that sored procedure do something, while functions do something and return result (variable or table).