Oracle ERROR: ORA-00900: invalid SQL statement - sql

I am trying to create the following datatype with 2 functions:
-- Employee
CREATE OR REPLACE TYPE EmployeeType AS OBJECT (
EmployeeNumber NUMBER,
EmployeeName VARCHAR2(150),
EmployeeAddress VARCHAR2(255),
MAP MEMBER FUNCTION getEmployeeNumber RETURN NUMBER,
MEMBER FUNCTION CalculateSalary RETURN FLOAT(2)
)
NOT FINAL;
CREATE OR REPLACE TYPE BODY EmployeeType AS
MAP MEMBER FUNCTION getEmployeeNumber RETURN NUMBER IS
BEGIN
RETURN EmployeeNumber;
END;
-- function that can be overriden by subtypes, make abstract
MEMBER FUNCTION CalculateSalary RETURN FLOAT(2) IS
BEGIN
-- function returns empty, has to be overwritten by fulltimeemployee
RETURN 0.00;
END;
END;
However I keep getting an error stating
ERROR: ORA-00900: invalid SQL statement
Error Code: 900
Query = END
I am using RazorSQL to execute my queries, I cant seem to get the line number causing this error but through trial and error I have found it to be one of the function descriptions in my TYPE BODY definition.
I have tried adding / after the last END; but it does not help solve the issue.

Replace FLOAT(2) with just FLOAT:
CREATE OR REPLACE TYPE EmployeeType AS OBJECT (
EmployeeNumber NUMBER,
EmployeeName VARCHAR2(150),
EmployeeAddress VARCHAR2(255),
MAP MEMBER FUNCTION getEmployeeNumber RETURN NUMBER,
MEMBER FUNCTION CalculateSalary RETURN FLOAT
)
NOT FINAL;
/
CREATE OR REPLACE TYPE BODY EmployeeType AS
MAP MEMBER FUNCTION getEmployeeNumber RETURN NUMBER IS
BEGIN
RETURN EmployeeNumber;
END;
-- function that can be overriden by subtypes, make abstract
MEMBER FUNCTION CalculateSalary RETURN FLOAT IS
BEGIN
-- function returns empty, has to be overwritten by fulltimeemployee
RETURN 0.00;
END;
END;
/
The documentation for CREATE TYPE doesn't mention this, but you can find the explanation in the topic related to CREATE FUNCTION : http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/create_function.htm
RETURN datatype
RETURN datatype For datatype, specify the data type of the return value of the function. The return value can have any data type
supported by PL/SQL.
............ The data type cannot specify a length, precision, or scale.
The database derives the length, precision, or scale of the return value
from the environment from which the function is called.

Related

How to define a type within a with block?

This code works:
WITH
FUNCTION a (a IN INTEGER)
RETURN INTEGER
IS
BEGIN
RETURN a + 1;
END;
b(v) AS (SELECT column_value FROM sys.ODCINUMBERLIST(1,2,3))
SELECT a (v) FROM b;
But I would like to define a type in this with statement. Later I want to reuse this type in order to use a pipelined function. Therefore I will need a type which is a table of a record. And the type must be defined outside of the function (not inside the function ) because the type will be returned by the function
I tried with this simple type w.
WITH
type w is record(w1 integer);
FUNCTION f (a in integer)
RETURN INTEGER
IS
ret integer;
BEGIN
return 2;
END;
B(b1) as (select 1 from dual)
select f(3) from dual;
It doesn't work:
[Error] Compilation (3: 5): ORA-00900: invalid SQL statement
Is it possible to define a type within a with statement and how can I do that?
The return data type of a function used in a SQL context (as opposed to PL/SQL) must be either a native SQL type or a schema-level user-defined type (a type defined on its own, with the create type statement, rather than defined in a package).
This restriction was not lifted when support for functions defined in the with clause was added in Oracle 12.1 - not even for the data type of a function so defined. In particular, the return data type can't be defined in the with clause of a SQL statement.
Then, records are supported only in PL/SQL; at the schema level, you will need to create an object type, rather than a record type.
NOTE: A pipelined function returning a collection of records (with the record type defined in a package) can be used in a SQL context; the reason is that Oracle defines a corresponding object type, at the schema level, behind the scenes, and takes care of the conversion. Why Oracle doesn't do the same for all functions is something only Oracle can explain. Of course, as you are finding out in another thread you started, pipelined table functions in the with clause are not supported (even though non-pipelined table functions are!)
We don't know the actual problem you are trying to solve, but it seems unlikely that you will be able to do everything you are trying to do in the with clause.
You cannot.
If you look at the SELECT documentation, particularly the syntax diagrams:
with_clause::=
plsql_declarations::=
You can see that you can only declare a function or a procedure.
If you try:
WITH FUNCTION f (a in integer)
RETURN w
IS
type w is record(w1 integer);
BEGIN
return w(2);
END;
SELECT f(3)
FROM DUAL;
You get the error:
ORA-06553: PLS-313: 'F' not declared in this scope
ORA-06552: PL/SQL: Item ignored
ORA-06553: PLS-498: illegal use of a type before its declaration
You cannot fix that error but, if you magically could (which you cannot using only local declarations), you would get a second error as a RECORD is a PL/SQL only data type and you are then trying to use it in an SQL scope.
For example, if you declared the type globally in a PL/SQL package:
CREATE PACKAGE pkg AS
type w is record(w1 integer);
END;
/
WITH FUNCTION f (a in integer)
RETURN pkg.w
IS
BEGIN
return pkg.w(2);
END;
SELECT f(3).w1
FROM DUAL;
The query gives the error:
ORA-00902: invalid datatype
You would need to use an SQL OBJECT type and declare it in the global SQL scope before running your query.
For example:
CREATE TYPE w IS OBJECT(w1 INTEGER);
WITH FUNCTION f (a in integer)
RETURN w
IS
BEGIN
return w(2);
END;
SELECT f(3).w1
FROM DUAL;
Outputs:
F(3).W1
2
To use a RECORD, you need to declare the type in a PL/SQL package or in a PL/SQL anonymous block and then use it only in PL/SQL.
For example, if you just want to run your function using a locally declared PL/SQL type then you can do it entirely in a PL/SQL anonymous block:
DECLARE
TYPE w IS record(w1 integer);
v_w w;
FUNCTION f (a in integer)
RETURN w
IS
BEGIN
return w(2);
END f;
BEGIN
v_w := f(3);
DBMS_OUTPUT.PUT_LINE(v_w.w1);
END;
/
Outputs:
2
db<>fiddle here
Your code will work if you define the type within the function like so:
WITH
FUNCTION f (a in integer)
RETURN INTEGER
IS
ret integer;
type w is record(w1 integer);
BEGIN
return 2;
END;
B(b1) as (select 1 from dual)
select f(3) from dual;

Oracle Object-Relational - How to get REF(SELF) in PLSQL

I need to select the REF(SELF) in a method of an object. For example:
CREATE TYPE SOMETYPE_TY AS OBJECT(
attr1 VARCHAR2(20),
attr2 DATE,
MEMBER PROCEDURE AddSomething(A1 VARCHAR2, A2 VARCHAR2)
) NOT FINAL;
How can I get the REF(SELF) in a method, like the following?
CREATE OR REPLACE TYPE BODY SOMETYPE_TY AS
MEMBER PROCEDURE AddSomething(A1 VARCHAR2, A2 VARCHAR2) AS
some_ref REF SOMETYPE_TY;
BEGIN
SELECT REF(SELF) INTO some_ref FROM DUAL;
END AddSomething;
END;
/
I tried with the DUAL table but it tells me ORA-24344: success with compilation error
You cannot. A REF is a pointer to a row in an object-derived table; it is not a pointer to an object.
From the REF documentation:
REF takes as its argument a correlation variable (table alias) associated with a row of an object table or an object view. A REF value is returned for the object instance that is bound to the variable or row.
To be able to get a REFerence to an object you need to put that object into a table and then get the REFerence to the table row.

PL/SQL - object which has method with parameter and returns sum of one type row

I am new in PL/SQL and having problem with PL/SQL
I want to write object method which has parameter shop name (in the table) and returns total amount of goods in shop.
Let`s say name of shop could be "Center".
CREATE TYPE SHOPS_TYP AS OBJECT (
S_NUM NUMBER,
S_NAME VARCHAR2(30),
TEL VARCHAR2(20)
);
/
CREATE TYPE GOODS_T AS OBJECT(
G_NUM NUMBER,
G_NAME VARCHAR2(30),
G_QUANTITY NUMBER,
G_PRICE NUMBER,
NUM_SHOP REF SHOPS_TYP,
ORDER MEMBER FUNCTION total(x GOODS_T) RETURN INTEGER
);
/
CREATE TABLE GOODS OF GOODS_TYP;
CREATE TYPE BODY GOODS_TYP AS
ORDER MEMBER FUNCTION total(x GOODS_TYP) RETURN INTEGER IS
BEGIN
RETURN (SELECT SUM(G.G_NUM) FROM GOODS G WHERE G.NUM_SHOP.S_NAME = x);
END;
END;
SELECT Value(A) from GOODS A where A.total('centers');
When I run the code above I am getting error in "RETURN" part of method. How can I implement this query ?
Return has to return an expression. The documentation defines expression as:
"an arbitrarily complex combination of operands (variables, constants, literals, operators, function invocations, and placeholders) and operators"
This does not include a result set, so change your method to look like this:
ORDER MEMBER FUNCTION total(x GOODS_TYP) RETURN INTEGER IS
return_value number;
BEGIN
SELECT SUM(G.G_NUM)
into return_value
FROM GOODS G WHERE G.NUM_SHOP.S_NAME = x;
RETURN return_value;
END;
By the way, you should make life easier for yourself and your colleagues by using one naming convention for your objects.

warning : function created with compilation error

Table EMP has ENAME as attribute.The following function gives error:
SET SERVEROUTPUT ON
SET ECHO ON
CREATE OR REPLACE FUNCTION count_emp(e_name varchar(20))
RETURN integer IS
total integer;
BEGIN
SELECT count(*) into total
FROM DEPARTMENTS
where ENAME = e_name;
RETURN total;
END;
/
warning:function created with compilation error.
You can run show errors; to see what compilation errors are.
The parameter's datatype should be specified without length. Also, use varchar2 instead of varchar.
From Oracle site:
The VARCHAR datatype is synonymous with the VARCHAR2 datatype. To avoid possible changes in behavior, always use the VARCHAR2 datatype to store variable-length character strings.
Try this:
CREATE OR REPLACE FUNCTION count_emp(e_name varchar2) -- here
RETURN integer IS
total integer;
BEGIN
SELECT count(*) into total
FROM DEPARTMENTS
where ENAME = e_name;
RETURN total;
END;
/
If you care about table EMP, you should use it in the function.
I would write this as:
CREATE OR REPLACE FUNCTION count_emp (
in_e_name varchar2
)
RETURN integer IS
v_total integer;
BEGIN
SELECT COUNT(*) into v_total
FROM EMP e
WHERE e.ENAME = in_e_name;
RETURN v_total;
END;
Notes:
Oracle will compile functions and stored procedures even when the objects don't (yet) exist. This is considered a "feature".
Use naming conventions to distinguish parameters and variables from columns. This is using in_ for the input parameters and v_ for the local variables.
Qualify all column name references. This further reduces the possibility of collision between a variable and column name.
You don't need a length for varchar2() inputs (which is preferable to varchar(), although perhaps one day, Oracle will cave to the standard).

SQL Method to retrieve a value

In SQL, how can I create a simple method to retrieve a value from an object?
Here is the code:
create type CurrentUser_objtyp as Object (
CurrUserName varchar2(20),
CurrUserPassword varchar2(20),
CurrUserType varchar2(15)
)
/
How can I retrieve the value of CurrUserName from a method?
As Oracle SQL types only allow public attributes there is no point in creating getter and setter methods for them.
But the general principle is to create member methods:
create type CurrentUser_objtyp as Object (
CurrUserName varchar2(20),
CurrUserPassword varchar2(20),
CurrUserType varchar2(15),
member function getname return varchar2
)
/
You need to write a body for the implementation, which is basically PL/SQL:
create type body CurrentUser_objtyp as
member function getname return varchar2
is
begin
return self.CurrUserName;
end;
end;
/
"How do I call the member function in your above code?"
To call a member function (or procedure) we first have to instantiate the object. There are various different ways of using objects, but again here is the simplest:
declare
currUser CurrentUser_objtyp := new CurrentUser_objtyp('MR KNOX', 'PASSWORD1', 'MANAGER');
begin
dbms_output.put_line('user name is '||currUser.getname);
end;
/
Oracle have devoted an entire manual to their Object-Relational constructs. You should read it to find out more.